diff mbox series

[ovs-dev,v3,14/16] northd: Add I-P for NB_Global and SB_Global.

Message ID 20231128023752.569985-1-numans@ovn.org
State Changes Requested
Headers show
Series northd lflow incremental processing | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed

Commit Message

Numan Siddique Nov. 28, 2023, 2:37 a.m. UTC
From: Numan Siddique <numans@ovn.org>

A new engine node "global_config" is added which handles the changes
to NB_Global an SB_Global tables.  It also creates these rows if
not present.

Without the I-P, any changes to the options column of these tables
result in recompute of 'northd' and 'lflow' engine nodes.

Signed-off-by: Numan Siddique <numans@ovn.org>
---
 northd/aging.c            |  21 +-
 northd/automake.mk        |   2 +
 northd/en-global-config.c | 588 ++++++++++++++++++++++++++++++++++++++
 northd/en-global-config.h |  65 +++++
 northd/en-lflow.c         |  11 +-
 northd/en-northd.c        |  52 ++--
 northd/en-northd.h        |   2 +-
 northd/en-sync-sb.c       |  22 +-
 northd/inc-proc-northd.c  |  38 ++-
 northd/northd.c           | 230 +++------------
 northd/northd.h           |  24 +-
 tests/ovn-northd.at       | 256 +++++++++++++++--
 12 files changed, 1014 insertions(+), 297 deletions(-)
 create mode 100644 northd/en-global-config.c
 create mode 100644 northd/en-global-config.h

Comments

Dumitru Ceara Dec. 13, 2023, 2:47 p.m. UTC | #1
On 11/28/23 03:37, numans@ovn.org wrote:
> From: Numan Siddique <numans@ovn.org>
> 
> A new engine node "global_config" is added which handles the changes
> to NB_Global an SB_Global tables.  It also creates these rows if
> not present.
> 
> Without the I-P, any changes to the options column of these tables
> result in recompute of 'northd' and 'lflow' engine nodes.
> 
> Signed-off-by: Numan Siddique <numans@ovn.org>
> ---
>  northd/aging.c            |  21 +-
>  northd/automake.mk        |   2 +
>  northd/en-global-config.c | 588 ++++++++++++++++++++++++++++++++++++++
>  northd/en-global-config.h |  65 +++++
>  northd/en-lflow.c         |  11 +-
>  northd/en-northd.c        |  52 ++--
>  northd/en-northd.h        |   2 +-
>  northd/en-sync-sb.c       |  22 +-
>  northd/inc-proc-northd.c  |  38 ++-
>  northd/northd.c           | 230 +++------------
>  northd/northd.h           |  24 +-
>  tests/ovn-northd.at       | 256 +++++++++++++++--
>  12 files changed, 1014 insertions(+), 297 deletions(-)

I think most of the options values we interpret in the the NB_Global and
SB_Global tables don't usually change (or don't change often).

Doesn't it make sense to not have "proper I-P" for these tables and
instead enhance northd_nb_nb_global_handler() to:

- if one of the well known NB_Global/SB_Global options change trigger a
recompute of the northd node
- if one of the other options change then do a plain NB -> SB options sync

I hope that can be done in way less than "1014 insertions(+), 297
deletions(-)".

What do you think?

Thanks,
Dumitru
Han Zhou Dec. 19, 2023, 1:36 a.m. UTC | #2
On Wed, Dec 13, 2023 at 6:47 AM Dumitru Ceara <dceara@redhat.com> wrote:
>
> On 11/28/23 03:37, numans@ovn.org wrote:
> > From: Numan Siddique <numans@ovn.org>
> >
> > A new engine node "global_config" is added which handles the changes
> > to NB_Global an SB_Global tables.  It also creates these rows if
> > not present.
> >
> > Without the I-P, any changes to the options column of these tables
> > result in recompute of 'northd' and 'lflow' engine nodes.

This is not true. Common updates to NB_Global and SB_Global, such as nb_cfg
and timestamps changes, or external_ids changes populated by ovn-k8s, will
not trigger recompute.
Could you be more specific what recompute are avoided by this patch? I can
see for example, IPSec option change is handled with I-P, but these are
really rare changes. It seems most other option changes and chassis feature
changes would still trigger recompute with this patch.

> >
> > Signed-off-by: Numan Siddique <numans@ovn.org>
> > ---
> >  northd/aging.c            |  21 +-
> >  northd/automake.mk        |   2 +
> >  northd/en-global-config.c | 588 ++++++++++++++++++++++++++++++++++++++
> >  northd/en-global-config.h |  65 +++++
> >  northd/en-lflow.c         |  11 +-
> >  northd/en-northd.c        |  52 ++--
> >  northd/en-northd.h        |   2 +-
> >  northd/en-sync-sb.c       |  22 +-
> >  northd/inc-proc-northd.c  |  38 ++-
> >  northd/northd.c           | 230 +++------------
> >  northd/northd.h           |  24 +-
> >  tests/ovn-northd.at       | 256 +++++++++++++++--
> >  12 files changed, 1014 insertions(+), 297 deletions(-)
>
> I think most of the options values we interpret in the the NB_Global and
> SB_Global tables don't usually change (or don't change often).
>
> Doesn't it make sense to not have "proper I-P" for these tables and
> instead enhance northd_nb_nb_global_handler() to:
>
> - if one of the well known NB_Global/SB_Global options change trigger a
> recompute of the northd node
> - if one of the other options change then do a plain NB -> SB options sync
>
> I hope that can be done in way less than "1014 insertions(+), 297
> deletions(-)".
>
> What do you think?
>

+1. And I am even questioning "well known NB_Global/SB_Global options
change". Are there really such changes that need to be done frequently in
production?

Besides, I have a comment to the function check_nb_options_out_of_sync():
why not simply check all options by comparing two SMAPs? Otherwise it would
be easy to miss the check for a newly added option. The function name also
looks like it checks all options instead of some selected ones. And the
criteria for the selection is not clear.

Thanks,
Han


> Thanks,
> Dumitru
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Numan Siddique Jan. 3, 2024, 9:18 p.m. UTC | #3
On Mon, Dec 18, 2023 at 8:36 PM Han Zhou <hzhou@ovn.org> wrote:
>
> On Wed, Dec 13, 2023 at 6:47 AM Dumitru Ceara <dceara@redhat.com> wrote:
> >
> > On 11/28/23 03:37, numans@ovn.org wrote:
> > > From: Numan Siddique <numans@ovn.org>
> > >
> > > A new engine node "global_config" is added which handles the changes
> > > to NB_Global an SB_Global tables.  It also creates these rows if
> > > not present.
> > >
> > > Without the I-P, any changes to the options column of these tables
> > > result in recompute of 'northd' and 'lflow' engine nodes.
>
> This is not true. Common updates to NB_Global and SB_Global, such as nb_cfg
> and timestamps changes, or external_ids changes populated by ovn-k8s, will
> not trigger recompute.

This commit talks about the changes to the options column of these
tables and not about
the common updates.  Any key added or deleted to the options column of
NB_Global and SB_Global results in full recompute.

Every few seconds (maybe its configurable) ovn-k writes a timestamp
value in the NB_Global's options column
and this results in 2 recompute nodes.  First for the NB_Global's
update and second when ovn-northd copies the
options column to the SB_Global.options and its update is received.

i,e

---
2024-01-03T14:47:42.976Z|05434|jsonrpc|DBG|tcp:10.89.0.4:6641:
received notification, method="update3",
params=[["monid","OVN_Northbound"],"00000000-0000-0000-0000-000000000000",{"NB_Global":{"2a7bbc45-90ab-4757-93c9-5f6ea4769c7f":{"modify":{"options":["map",[["e2e_timestamp","1704293262"]]]}}}}]
2024-01-03T14:47:42.976Z|00156|poll_loop(stopwatch0)|DBG|wakeup due to
[POLLIN] on fd 10 (FIFO pipe:[79138091]) at lib/stopwatch.c:456
2024-01-03T14:47:42.976Z|05435|inc_proc_eng|DBG|Initializing new run

2024-01-03T14:47:42.977Z|00157|poll_loop(stopwatch0)|DBG|wakeup due to
[POLLIN] on fd 10 (FIFO pipe:[79138091]) at lib/stopwatch.c:456
2024-01-03T14:47:42.977Z|00158|poll_loop(stopwatch0)|DBG|wakeup due to
[POLLIN] on fd 10 (FIFO pipe:[79138091]) at lib/stopwatch.c:456
2024-01-03T14:47:42.977Z|05510|inc_proc_eng|DBG|northd/en-northd.c:131:
node: northd, old_state Stale, new_state Updated
2024-01-03T14:47:42.977Z|05511|inc_proc_eng|DBG|node: northd,
recompute (failed handler for input NB_nb_global) took 0ms

# recomputes here
2024-01-03T14:47:42.979Z|05555|jsonrpc|DBG|tcp:10.89.0.4:6642: send
request, method="transact",
params=["OVN_Southbound",{"where":[["_uuid","==",["uuid","cb81dba5-4bff-49e0-ae9f-107a43616b77"]]],"row":{"options":["map",[["e2e_timestamp","1704293262"],["mac_prefix","9e:c3:b0"],["max_tunid","16711680"],["name","global"],["northd-backoff-interval-ms","300"],["northd_internal_version","23.09.2-20.29.0-71.6"],["northd_probe_interval","5000"],["svc_monitor_mac","f2:ba:19:99:f1:17"]]]},"op":"update","table":"SB_Global"},{"comment":"ovn-northd","op":"comment"},{"lock":"ovn_northd","op":"assert"}],
id=228
2024-01-03T14:47:42.979Z|00165|poll_loop(stopwatch0)|DBG|wakeup due to
[POLLIN] on fd 10 (FIFO pipe:[79138091]) at lib/stopwatch.c:456
2024-01-03T14:47:42.980Z|05556|poll_loop|DBG|wakeup due to [POLLIN] on
fd 17 (10.89.0.4:37450<->10.89.0.4:6642) at lib/stream-fd.c:157 (0%
CPU usage)
2024-01-03T14:47:42.980Z|00166|poll_loop(stopwatch0)|DBG|wakeup due to
[POLLIN] on fd 10 (FIFO pipe:[79138091]) at lib/stopwatch.c:456
2024-01-03T14:47:42.980Z|05557|jsonrpc|DBG|tcp:10.89.0.4:6642:
received notification, method="update3",
params=[["monid","OVN_Southbound"],"00000000-0000-0000-0000-000000000000",{"SB_Global":{"cb81dba5-4bff-49e0-ae9f-107a43616b77":{"modify":{"options":["map",[["e2e_timestamp","1704293262"]]]}}}}]
2024-01-03T14:47:42.980Z|05558|jsonrpc|DBG|tcp:10.89.0.4:6642:
received reply, result=[{"count":1},{},{}], id=228

# recompute again here
----

> Could you be more specific what recompute are avoided by this patch? I can
> see for example, IPSec option change is handled with I-P, but these are
> really rare changes. It seems most other option changes and chassis feature
> changes would still trigger recompute with this patch.

Right now with the present main,  we fall back to full recompute twice
  1.  CMS writes some key-value to NB_Global.options
  2.  ovn-northd does a full recompute
  3.  ovn-northd syncs NB_Global.options to SB_Global.options
  4. ovn-northd again does a full recompute when it receives the
update for the SB_Global.options transaction.


>
> > >
> > > Signed-off-by: Numan Siddique <numans@ovn.org>
> > > ---
> > >  northd/aging.c            |  21 +-
> > >  northd/automake.mk        |   2 +
> > >  northd/en-global-config.c | 588 ++++++++++++++++++++++++++++++++++++++
> > >  northd/en-global-config.h |  65 +++++
> > >  northd/en-lflow.c         |  11 +-
> > >  northd/en-northd.c        |  52 ++--
> > >  northd/en-northd.h        |   2 +-
> > >  northd/en-sync-sb.c       |  22 +-
> > >  northd/inc-proc-northd.c  |  38 ++-
> > >  northd/northd.c           | 230 +++------------
> > >  northd/northd.h           |  24 +-
> > >  tests/ovn-northd.at       | 256 +++++++++++++++--
> > >  12 files changed, 1014 insertions(+), 297 deletions(-)
> >
> > I think most of the options values we interpret in the the NB_Global and
> > SB_Global tables don't usually change (or don't change often).
> >
> > Doesn't it make sense to not have "proper I-P" for these tables and
> > instead enhance northd_nb_nb_global_handler() to:
> >
> > - if one of the well known NB_Global/SB_Global options change trigger a
> > recompute of the northd node
> > - if one of the other options change then do a plain NB -> SB options sync
> >
> > I hope that can be done in way less than "1014 insertions(+), 297
> > deletions(-)".
> >
> > What do you think?

I did think of this.  But  even to do this, we need to store the
config smap in some engine node data
(maybe northd engine node) and compare each config option (which we
are interested in) with the
stored one and see if it has changed or not.  The function
check_nb_options_out_of_sync() in this patch
already does that.

And even to sync from NB_Global.options to SB_Global.options would
require some amount of code
even if we go with your suggested approach.

Right now with the present main,  we fall back to full recompute twice
  1.  CMS writes some key-value to NB_Global.options
  2.  ovn-northd does a full recompute
  3.  ovn-northd syncs NB_Global.options to SB_Global.options
  4. ovn-northd again does a full recompute when it receives the
update for the SB_Global.options transaction.

With your suggested approach, we still need to solve (4).  That's the
reason I thought of having a separate
engine to handle all these.  And it made sense to also handle the
Chassis changes for the chassis_features.


> >
>
> +1. And I am even questioning "well known NB_Global/SB_Global options
> change". Are there really such changes that need to be done frequently in
> production?

They don't change frequently.  But the issue is that CMS can write any
key-value to NB_Global.options
and we fall back to recompute twice.  Please see above.


>
> Besides, I have a comment to the function check_nb_options_out_of_sync():
> why not simply check all options by comparing two SMAPs?

We do  check the smaps  first.  If no change, then it would be a no-op.

But if the NB_Global.options gets modified due to any foo=bar key
value, we resort
to full recompute and this patch tries to solve this.
This patch checks for all the supported options/keys which we care
about in the NB_Global.options
and if any of these changes we update the newly added engine node and
in the tracked data
we if the NB options (which we care about changed) or SB chassis
features changed.

The nodes which depend on this global node data,  can either ignore
these changes,  handle these changes
or fall back to full recompute.



Otherwise it would
> be easy to miss the check for a newly added option. The function name also
> looks like it checks all options instead of some selected ones. And the
> criteria for the selection is not clear.

The criteria is simple.  We check for all the supported options in
NB_Global.options.
In the future if we add new options we need to enhance this function
to add something like

---
if (config_out_of_sync(&nb->options, &config_data->nb_options,
    "newly_added_foo_option", false)) {
    return true;
}
---

And I think that's reasonable to expect the author to add this code.

Thanks
Numan


>
> Thanks,
> Han
>
>
> > Thanks,
> > Dumitru
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
diff mbox series

Patch

diff --git a/northd/aging.c b/northd/aging.c
index f626c72c8c..cf988b39c4 100644
--- a/northd/aging.c
+++ b/northd/aging.c
@@ -15,6 +15,7 @@ 
 
 #include <config.h>
 
+#include "en-global-config.h"
 #include "lib/inc-proc-eng.h"
 #include "lib/ovn-nb-idl.h"
 #include "lib/ovn-sb-idl.h"
@@ -100,15 +101,10 @@  aging_context_handle_timestamp(struct aging_context *ctx, int64_t timestamp)
 static uint32_t
 get_removal_limit(struct engine_node *node, const char *name)
 {
-    const struct nbrec_nb_global_table *nb_global_table =
-            EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
-    const struct nbrec_nb_global *nb =
-            nbrec_nb_global_table_first(nb_global_table);
-    if (!nb) {
-        return 0;
-    }
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
 
-    return smap_get_uint(&nb->options, name, 0);
+    return smap_get_uint(&global_config->nb_options, name, 0);
 }
 
 /* MAC binding aging */
@@ -142,11 +138,14 @@  en_mac_binding_aging_run(struct engine_node *node, void *data OVS_UNUSED)
 {
     const struct engine_context *eng_ctx = engine_get_context();
     struct northd_data *northd_data = engine_get_input_data("northd", node);
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
+
     struct aging_waker *waker =
         engine_get_input_data("mac_binding_aging_waker", node);
 
     if (!eng_ctx->ovnsb_idl_txn ||
-        !northd_data->features.mac_binding_timestamp ||
+        !global_config->features.mac_binding_timestamp ||
         time_msec() < waker->next_wake_msec) {
         return;
     }
@@ -271,9 +270,11 @@  en_fdb_aging_run(struct engine_node *node, void *data OVS_UNUSED)
     const struct engine_context *eng_ctx = engine_get_context();
     struct northd_data *northd_data = engine_get_input_data("northd", node);
     struct aging_waker *waker = engine_get_input_data("fdb_aging_waker", node);
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
 
     if (!eng_ctx->ovnsb_idl_txn ||
-        !northd_data->features.fdb_timestamp ||
+        !global_config->features.fdb_timestamp ||
         time_msec() < waker->next_wake_msec) {
         return;
     }
diff --git a/northd/automake.mk b/northd/automake.mk
index aac6a8ecdd..534b31c4dd 100644
--- a/northd/automake.mk
+++ b/northd/automake.mk
@@ -8,6 +8,8 @@  northd_ovn_northd_SOURCES = \
 	northd/northd.c \
 	northd/northd.h \
 	northd/ovn-northd.c \
+	northd/en-global-config.c \
+	northd/en-global-config.h \
 	northd/en-northd.c \
 	northd/en-northd.h \
 	northd/en-lflow.c \
diff --git a/northd/en-global-config.c b/northd/en-global-config.c
new file mode 100644
index 0000000000..0d218f2ab5
--- /dev/null
+++ b/northd/en-global-config.c
@@ -0,0 +1,588 @@ 
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* OVS includes */
+#include "openvswitch/vlog.h"
+
+/* OVN includes */
+#include "debug.h"
+#include "en-global-config.h"
+#include "include/ovn/features.h"
+#include "ipam.h"
+#include "lib/ovn-nb-idl.h"
+#include "lib/ovn-sb-idl.h"
+#include "northd.h"
+
+
+VLOG_DEFINE_THIS_MODULE(en_global_config);
+
+/* static function declarations. */
+static void northd_enable_all_features(struct ed_type_global_config *);
+static void build_chassis_features(const struct sbrec_chassis_table *,
+                                   struct chassis_features *);
+static bool chassis_features_changed(const struct chassis_features *,
+                                     const struct chassis_features *);
+static bool config_out_of_sync(const struct smap *config,
+                               const struct smap *saved_config,
+                               const char *key, bool must_be_present);
+static bool check_nb_options_out_of_sync(const struct nbrec_nb_global *,
+                                         struct ed_type_global_config *);
+static void update_sb_config_options_to_sbrec(struct ed_type_global_config *,
+                                              const struct sbrec_sb_global *);
+
+void *
+en_global_config_init(struct engine_node *node OVS_UNUSED,
+                      struct engine_arg *args OVS_UNUSED)
+{
+    struct ed_type_global_config *data = xzalloc(sizeof *data);
+    smap_init(&data->nb_options);
+    smap_init(&data->sb_options);
+    northd_enable_all_features(data);
+    return data;
+}
+
+void
+en_global_config_run(struct engine_node *node , void *data)
+{
+    const struct engine_context *eng_ctx = engine_get_context();
+    if (!eng_ctx->ovnnb_idl_txn || !eng_ctx->ovnsb_idl_txn) {
+        return;
+    }
+
+    const struct nbrec_nb_global_table *nb_global_table =
+        EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
+    const struct sbrec_sb_global_table *sb_global_table =
+        EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
+    const struct sbrec_chassis_table *sbrec_chassis_table =
+        EN_OVSDB_GET(engine_get_input("SB_chassis", node));
+
+    en_global_config_clear_tracked_data(data);
+
+    struct ed_type_global_config *config_data = data;
+
+    /* Sync ipsec configuration.
+     * Copy nb_cfg from northbound to southbound database.
+     * Also set up to update sb_cfg once our southbound transaction commits. */
+    const struct nbrec_nb_global *nb =
+        nbrec_nb_global_table_first(nb_global_table);
+    if (!nb) {
+        nb = nbrec_nb_global_insert(eng_ctx->ovnnb_idl_txn);
+    }
+
+    const char *mac_addr_prefix = set_mac_prefix(smap_get(&nb->options,
+                                                          "mac_prefix"));
+
+    const char *monitor_mac = smap_get(&nb->options, "svc_monitor_mac");
+    if (monitor_mac) {
+        if (eth_addr_from_string(monitor_mac,
+                                 &config_data->svc_monitor_mac_ea)) {
+            snprintf(config_data->svc_monitor_mac,
+                     sizeof config_data->svc_monitor_mac,
+                     ETH_ADDR_FMT,
+                     ETH_ADDR_ARGS(config_data->svc_monitor_mac_ea));
+        } else {
+            monitor_mac = NULL;
+        }
+    }
+
+    struct smap *options = &config_data->nb_options;
+    smap_destroy(options);
+    smap_clone(options, &nb->options);
+
+    smap_replace(options, "mac_prefix", mac_addr_prefix);
+
+    if (!monitor_mac) {
+        eth_addr_random(&config_data->svc_monitor_mac_ea);
+        snprintf(config_data->svc_monitor_mac,
+                 sizeof config_data->svc_monitor_mac, ETH_ADDR_FMT,
+                 ETH_ADDR_ARGS(config_data->svc_monitor_mac_ea));
+        smap_replace(options, "svc_monitor_mac",
+                     config_data->svc_monitor_mac);
+    }
+
+    char *max_tunid = xasprintf("%d",
+        get_ovn_max_dp_key_local(sbrec_chassis_table));
+    smap_replace(options, "max_tunid", max_tunid);
+    free(max_tunid);
+
+    char *ovn_internal_version = ovn_get_internal_version();
+    if (strcmp(ovn_internal_version,
+                smap_get_def(options, "northd_internal_version", ""))) {
+        smap_replace(options, "northd_internal_version",
+                     ovn_internal_version);
+        config_data->ovn_internal_version_changed = true;
+    } else {
+        config_data->ovn_internal_version_changed = false;
+    }
+
+    free(ovn_internal_version);
+
+    if (!smap_equal(&nb->options, options)) {
+        nbrec_nb_global_verify_options(nb);
+        nbrec_nb_global_set_options(nb, options);
+    }
+
+    if (smap_get_bool(&nb->options, "ignore_chassis_features", false)) {
+        northd_enable_all_features(config_data);
+    } else {
+        build_chassis_features(sbrec_chassis_table, &config_data->features);
+    }
+
+    init_debug_config(nb);
+
+    const struct sbrec_sb_global *sb =
+        sbrec_sb_global_table_first(sb_global_table);
+    if (!sb) {
+        sb = sbrec_sb_global_insert(eng_ctx->ovnsb_idl_txn);
+    }
+    if (nb->ipsec != sb->ipsec) {
+        sbrec_sb_global_set_ipsec(sb, nb->ipsec);
+    }
+
+    /* Set up SB_Global (depends on chassis features). */
+    update_sb_config_options_to_sbrec(config_data, sb);
+
+    engine_set_node_state(node, EN_UPDATED);
+}
+
+void en_global_config_cleanup(void *data OVS_UNUSED)
+{
+    struct ed_type_global_config *config_data = data;
+    smap_destroy(&config_data->nb_options);
+    smap_destroy(&config_data->sb_options);
+    destroy_debug_config();
+}
+
+void
+en_global_config_clear_tracked_data(void *data)
+{
+    struct ed_type_global_config *config_data = data;
+    config_data->tracked = false;
+    config_data->tracked_data.nb_options_changed = false;
+    config_data->tracked_data.chassis_features_changed = false;
+}
+
+bool
+global_config_nb_global_handler(struct engine_node *node, void *data)
+{
+    const struct nbrec_nb_global_table *nb_global_table =
+        EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
+    const struct sbrec_sb_global_table *sb_global_table =
+        EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
+
+    const struct nbrec_nb_global *nb =
+        nbrec_nb_global_table_first(nb_global_table);
+    if (!nb) {
+        return false;
+    }
+
+    const struct sbrec_sb_global *sb =
+        sbrec_sb_global_table_first(sb_global_table);
+    if (!sb) {
+        return false;
+    }
+
+    /* We are only interested in ipsec and options column. */
+    bool changes_relevant = false;
+    if (nbrec_nb_global_is_updated(nb, NBREC_NB_GLOBAL_COL_IPSEC)
+        || nbrec_nb_global_is_updated(nb, NBREC_NB_GLOBAL_COL_OPTIONS)) {
+        changes_relevant = true;
+    }
+
+    if (!changes_relevant) {
+        return true;
+    }
+
+    const struct engine_context *eng_ctx = engine_get_context();
+    if (!eng_ctx->ovnsb_idl_txn) {
+        return false;
+    }
+
+    if (nb->ipsec != sb->ipsec) {
+        sbrec_sb_global_set_ipsec(sb, nb->ipsec);
+    }
+
+    struct ed_type_global_config *config_data = data;
+    config_data->tracked = true;
+
+    if (smap_equal(&nb->options, &config_data->nb_options)) {
+        return true;
+    }
+
+    /* Return false if an option is out of sync and requires updating the
+     * NB config. (Like svc_monitor_mac, max_tunid and mac_prefix). */
+    /* Check if svc_monitor_mac has changed or not. */
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "svc_monitor_mac", true)) {
+        return false;
+    }
+
+    /* Check if max_tunid has changed or not. */
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "max_tunid", true)) {
+        return false;
+    }
+
+    /* Check if mac_prefix has changed or not. */
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "mac_prefix", true)) {
+        return false;
+    }
+
+    /* Check if ignore_chassis_features has changed or not. */
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "ignore_chassis_features", false)) {
+        return false;
+    }
+
+    /* Check if northd_internal_version has changed or not. */
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "northd_internal_version", false)) {
+        return false;
+    }
+
+    if (check_nb_options_out_of_sync(nb, config_data)) {
+        config_data->tracked_data.nb_options_changed = true;
+    }
+
+    smap_destroy(&config_data->nb_options);
+    smap_clone(&config_data->nb_options, &nb->options);
+
+    update_sb_config_options_to_sbrec(config_data, sb);
+
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
+bool
+global_config_sb_global_handler(struct engine_node *node, void *data)
+{
+    const struct sbrec_sb_global_table *sb_global_table =
+        EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
+
+    const struct sbrec_sb_global *sb =
+        sbrec_sb_global_table_first(sb_global_table);
+    if (!sb) {
+        return false;
+    }
+
+    struct ed_type_global_config *config_data = data;
+
+    if (!smap_equal(&sb->options, &config_data->sb_options)) {
+        return false;
+    }
+
+    /* No need to update the engine node. */
+    return true;
+}
+
+bool
+global_config_sb_chassis_handler(struct engine_node *node, void *data)
+{
+    struct ed_type_global_config *config_data = data;
+
+    const struct sbrec_chassis_table *sbrec_chassis_table =
+        EN_OVSDB_GET(engine_get_input("SB_chassis", node));
+    const struct sbrec_chassis *chassis;
+
+    SBREC_CHASSIS_TABLE_FOR_EACH_TRACKED (chassis, sbrec_chassis_table) {
+        if (sbrec_chassis_is_new(chassis)
+            || sbrec_chassis_is_deleted(chassis)
+            || sbrec_chassis_is_updated(chassis,
+                                        SBREC_CHASSIS_COL_ENCAPS)) {
+            return false;
+        }
+
+        for (size_t i = 0; i < chassis->n_encaps; i++) {
+            if (sbrec_encap_row_get_seqno(chassis->encaps[i],
+                                          OVSDB_IDL_CHANGE_MODIFY) > 0) {
+                return false;
+            }
+        }
+    }
+
+    if (smap_get_bool(&config_data->nb_options, "ignore_chassis_features",
+                      false)) {
+        return true;
+    }
+
+    bool reevaluate_chassis_features = false;
+
+    /* Check and evaluate chassis features. */
+    SBREC_CHASSIS_TABLE_FOR_EACH_TRACKED (chassis, sbrec_chassis_table) {
+        if (sbrec_chassis_is_updated(chassis,
+                                        SBREC_CHASSIS_COL_OTHER_CONFIG)) {
+            reevaluate_chassis_features = true;
+            break;
+        }
+    }
+
+    if (!reevaluate_chassis_features) {
+        return true;
+    }
+
+    struct chassis_features present_features = config_data->features;
+
+    /* Enable all features before calling build_chassis_features() as
+    * build_chassis_features() only sets the feature flags to false. */
+    northd_enable_all_features(config_data);
+    build_chassis_features(sbrec_chassis_table, &config_data->features);
+
+    if (chassis_features_changed(&present_features, &config_data->features)) {
+        config_data->tracked_data.chassis_features_changed = true;
+        config_data->tracked = true;
+        engine_set_node_state(node, EN_UPDATED);
+    }
+
+    return true;
+}
+
+/* generic global config handler for any engine node which has global_config
+ * has an input node . */
+bool
+node_global_config_handler(struct engine_node *node, void *data OVS_UNUSED)
+{
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
+
+    if (!global_config->tracked
+        || global_config->tracked_data.chassis_features_changed
+        || global_config->tracked_data.nb_options_changed) {
+        return false;
+    }
+
+    return true;
+}
+
+/* static functions. */
+static void
+northd_enable_all_features(struct ed_type_global_config *data)
+{
+    data->features = (struct chassis_features) {
+        .ct_no_masked_label = true,
+        .mac_binding_timestamp = true,
+        .ct_lb_related = true,
+        .fdb_timestamp = true,
+        .ls_dpg_column = true,
+    };
+}
+
+static void
+build_chassis_features(const struct sbrec_chassis_table *sbrec_chassis_table,
+                       struct chassis_features *chassis_features)
+{
+    const struct sbrec_chassis *chassis;
+
+    SBREC_CHASSIS_TABLE_FOR_EACH (chassis, sbrec_chassis_table) {
+        /* Only consider local AZ chassis.  Remote ones don't install
+         * flows generated by the local northd.
+         */
+        if (smap_get_bool(&chassis->other_config, "is-remote", false)) {
+            continue;
+        }
+
+        bool ct_no_masked_label =
+            smap_get_bool(&chassis->other_config,
+                          OVN_FEATURE_CT_NO_MASKED_LABEL,
+                          false);
+        if (!ct_no_masked_label && chassis_features->ct_no_masked_label) {
+            chassis_features->ct_no_masked_label = false;
+        }
+
+        bool mac_binding_timestamp =
+            smap_get_bool(&chassis->other_config,
+                          OVN_FEATURE_MAC_BINDING_TIMESTAMP,
+                          false);
+        if (!mac_binding_timestamp &&
+            chassis_features->mac_binding_timestamp) {
+            chassis_features->mac_binding_timestamp = false;
+        }
+
+        bool ct_lb_related =
+            smap_get_bool(&chassis->other_config,
+                          OVN_FEATURE_CT_LB_RELATED,
+                          false);
+        if (!ct_lb_related &&
+            chassis_features->ct_lb_related) {
+            chassis_features->ct_lb_related = false;
+        }
+
+        bool fdb_timestamp =
+            smap_get_bool(&chassis->other_config,
+                          OVN_FEATURE_FDB_TIMESTAMP,
+                          false);
+        if (!fdb_timestamp &&
+            chassis_features->fdb_timestamp) {
+            chassis_features->fdb_timestamp = false;
+        }
+
+        bool ls_dpg_column =
+            smap_get_bool(&chassis->other_config,
+                          OVN_FEATURE_LS_DPG_COLUMN,
+                          false);
+        if (!ls_dpg_column &&
+            chassis_features->ls_dpg_column) {
+            chassis_features->ls_dpg_column = false;
+        }
+    }
+}
+
+static bool
+config_out_of_sync(const struct smap *config, const struct smap *saved_config,
+                   const char *key, bool must_be_present)
+{
+    const char *value = smap_get(config, key);
+    if (!value && must_be_present) {
+        return true;
+    }
+
+    const char *saved_value = smap_get(saved_config, key);
+    if (!saved_value && must_be_present) {
+        return true;
+    }
+
+    if (!value && !saved_value) {
+        return false;
+    }
+
+    if ((!value && saved_value) || (value && !saved_value)) {
+        return true;
+    }
+
+    return strcmp(value, saved_value);
+}
+
+static bool
+check_nb_options_out_of_sync(const struct nbrec_nb_global *nb,
+                             struct ed_type_global_config *config_data)
+{
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "mac_binding_removal_limit", false)) {
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "fdb_removal_limit", false)) {
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "controller_event", false)) {
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "ignore_lsp_down", false)) {
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "use_ct_inv_match", false)) {
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "default_acl_drop", false)) {
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "debug_drop_domain_id", false)) {
+        init_debug_config(nb);
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "debug_drop_collector_set", false)) {
+        init_debug_config(nb);
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "use_common_zone", false)) {
+        return true;
+    }
+
+    if (config_out_of_sync(&nb->options, &config_data->nb_options,
+                           "install_ls_lb_from_router", false)) {
+        return true;
+    }
+
+    return false;
+}
+
+static void
+update_sb_config_options_to_sbrec(struct ed_type_global_config *config_data,
+                                  const struct sbrec_sb_global *sb)
+{
+    struct smap *options = &config_data->sb_options;
+
+    smap_destroy(options);
+    smap_clone(options, &config_data->nb_options);
+
+    /* Inform ovn-controllers whether LB flows will use ct_mark (i.e., only
+     * if all chassis support it).  If not explicitly present in the database
+     * the default value to be used for this option is 'true'.
+     */
+    if (!config_data->features.ct_no_masked_label) {
+        smap_replace(options, "lb_hairpin_use_ct_mark", "false");
+    } else {
+        smap_remove(options, "lb_hairpin_use_ct_mark");
+    }
+
+    /* Hackaround SB_global.options overwrite by NB_Global.options for
+     * 'sbctl_probe_interval' option.
+     */
+    const char *sip = smap_get(&sb->options, "sbctl_probe_interval");
+    if (sip) {
+        smap_replace(options, "sbctl_probe_interval", sip);
+    }
+
+    if (!smap_equal(&sb->options, options)) {
+        sbrec_sb_global_set_options(sb, options);
+    }
+}
+
+static bool
+chassis_features_changed(const struct chassis_features *present,
+                         const struct chassis_features *updated)
+{
+    if (present->ct_no_masked_label != updated->ct_no_masked_label) {
+        return true;
+    }
+
+    if (present->mac_binding_timestamp != updated->mac_binding_timestamp) {
+        return true;
+    }
+
+    if (present->ct_lb_related != updated->ct_lb_related) {
+        return true;
+    }
+
+    if (present->fdb_timestamp != updated->fdb_timestamp) {
+        return true;
+    }
+
+    if (present->ls_dpg_column != updated->ls_dpg_column) {
+        return true;
+    }
+
+    return false;
+}
diff --git a/northd/en-global-config.h b/northd/en-global-config.h
new file mode 100644
index 0000000000..436bc7fa35
--- /dev/null
+++ b/northd/en-global-config.h
@@ -0,0 +1,65 @@ 
+#ifndef EN_GLOBAL_CONFIG_H
+#define EN_GLOBAL_CONFIG_H 1
+
+#include <config.h>
+
+/* OVS includes. */
+#include "lib/packets.h"
+#include "lib/smap.h"
+
+/* OVN includes. */
+#include "lib/inc-proc-eng.h"
+
+struct nbrec_nb_global;
+struct sbrec_sb_global;
+
+struct chassis_features {
+    bool ct_no_masked_label;
+    bool mac_binding_timestamp;
+    bool ct_lb_related;
+    bool fdb_timestamp;
+    bool ls_dpg_column;
+};
+
+struct global_config_tracked_data {
+    bool nb_options_changed;
+    bool chassis_features_changed;
+};
+
+/* struct which maintains the data of the engine node global_config. */
+struct ed_type_global_config {
+    struct smap nb_options;
+    struct smap sb_options;
+    const struct nbrec_nb_global *nb_global;
+    const struct sbrec_sb_global *sb_global;
+
+    /* MAC allocated for service monitor usage. Just one mac is allocated
+     * for this purpose and ovn-controller's on each chassis will make use
+     * of this mac when sending out the packets to monitor the services
+     * defined in Service_Monitor Southbound table. Since these packets
+     * are locally handled, having just one mac is good enough. */
+    char svc_monitor_mac[ETH_ADDR_STRLEN + 1];
+    struct eth_addr svc_monitor_mac_ea;
+
+    struct chassis_features features;
+
+    bool ovn_internal_version_changed;
+
+    bool tracked;
+    struct global_config_tracked_data tracked_data;
+};
+
+void *en_global_config_init(struct engine_node *, struct engine_arg *);
+void en_global_config_run(struct engine_node *, void *data);
+void en_global_config_cleanup(void *data);
+void en_global_config_clear_tracked_data(void *data);
+
+bool global_config_nb_global_handler(struct engine_node *, void *data);
+bool global_config_sb_global_handler(struct engine_node *, void *data);
+bool global_config_sb_chassis_handler(struct engine_node *, void *data);
+
+/* generic global config handler for any engine node which has global_config
+ * has an input node . */
+bool node_global_config_handler(struct engine_node *, void *data);
+
+#endif /* EN_GLOBAL_CONFIG_H */
diff --git a/northd/en-lflow.c b/northd/en-lflow.c
index 784c3c2efb..1f4cf94c28 100644
--- a/northd/en-lflow.c
+++ b/northd/en-lflow.c
@@ -18,6 +18,7 @@ 
 #include <stdlib.h>
 #include <stdio.h>
 
+#include "en-global-config.h"
 #include "en-lflow.h"
 #include "en-lr-nat.h"
 #include "en-lr-stateful.h"
@@ -77,10 +78,14 @@  lflow_get_input_data(struct engine_node *node,
     lflow_input->meter_groups = &sync_meters_data->meter_groups;
     lflow_input->lb_datapaths_map = &northd_data->lb_datapaths_map;
     lflow_input->svc_monitor_map = &northd_data->svc_monitor_map;
-    lflow_input->features = &northd_data->features;
-    lflow_input->ovn_internal_version_changed =
-                      northd_data->ovn_internal_version_changed;
     lflow_input->bfd_connections = NULL;
+
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
+    lflow_input->features = &global_config->features;
+    lflow_input->ovn_internal_version_changed =
+                      global_config->ovn_internal_version_changed;
+    lflow_input->svc_monitor_mac = global_config->svc_monitor_mac;
 }
 
 void en_lflow_run(struct engine_node *node, void *data)
diff --git a/northd/en-northd.c b/northd/en-northd.c
index 5143603f39..4479b4aff2 100644
--- a/northd/en-northd.c
+++ b/northd/en-northd.c
@@ -19,6 +19,7 @@ 
 #include <stdio.h>
 
 #include "coverage.h"
+#include "en-global-config.h"
 #include "en-northd.h"
 #include "en-lb-data.h"
 #include "lib/inc-proc-eng.h"
@@ -65,8 +66,6 @@  northd_get_input_data(struct engine_node *node,
             engine_get_input("SB_fdb", node),
             "sbrec_fdb_by_dp_and_port");
 
-    input_data->nbrec_nb_global_table =
-        EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
     input_data->nbrec_logical_switch_table =
         EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
     input_data->nbrec_logical_router_table =
@@ -78,8 +77,6 @@  northd_get_input_data(struct engine_node *node,
     input_data->nbrec_mirror_table =
         EN_OVSDB_GET(engine_get_input("NB_mirror", node));
 
-    input_data->sbrec_sb_global_table =
-        EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
     input_data->sbrec_datapath_binding_table =
         EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
     input_data->sbrec_port_binding_table =
@@ -109,6 +106,14 @@  northd_get_input_data(struct engine_node *node,
         engine_get_input_data("lb_data", node);
     input_data->lbs = &lb_data->lbs;
     input_data->lbgrps = &lb_data->lbgrps;
+
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
+    input_data->nb_options = &global_config->nb_options;
+    input_data->sb_options = &global_config->sb_options;
+    input_data->svc_monitor_mac = global_config->svc_monitor_mac;
+    input_data->svc_monitor_mac_ea = global_config->svc_monitor_mac_ea;
+    input_data->features = &global_config->features;
 }
 
 void
@@ -129,31 +134,6 @@  en_northd_run(struct engine_node *node, void *data)
                  eng_ctx->ovnsb_idl_txn);
     stopwatch_stop(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());
     engine_set_node_state(node, EN_UPDATED);
-
-}
-
-bool
-northd_nb_nb_global_handler(struct engine_node *node,
-                            void *data OVS_UNUSED)
-{
-    const struct nbrec_nb_global_table *nb_global_table
-        = EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
-
-    const struct nbrec_nb_global *nb =
-        nbrec_nb_global_table_first(nb_global_table);
-
-    if (!nb) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-        VLOG_WARN_RL(&rl, "NB_Global is updated but has no record.");
-        return false;
-    }
-
-    /* We care about the 'options' and 'ipsec' columns only. */
-    if (nbrec_nb_global_is_updated(nb, NBREC_NB_GLOBAL_COL_OPTIONS) ||
-        nbrec_nb_global_is_updated(nb, NBREC_NB_GLOBAL_COL_IPSEC)) {
-        return false;
-    }
-    return true;
 }
 
 bool
@@ -242,6 +222,20 @@  northd_lb_data_handler(struct engine_node *node, void *data)
     return true;
 }
 
+bool
+northd_global_config_handler(struct engine_node *node, void *data OVS_UNUSED)
+{
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
+
+    if (!global_config->tracked
+        || global_config->tracked_data.nb_options_changed) {
+        return false;
+    }
+
+    return true;
+}
+
 void
 *en_northd_init(struct engine_node *node OVS_UNUSED,
                 struct engine_arg *arg OVS_UNUSED)
diff --git a/northd/en-northd.h b/northd/en-northd.h
index 5a88871760..9b7bda32aa 100644
--- a/northd/en-northd.h
+++ b/northd/en-northd.h
@@ -14,7 +14,7 @@  void *en_northd_init(struct engine_node *node OVS_UNUSED,
                      struct engine_arg *arg);
 void en_northd_cleanup(void *data);
 void en_northd_clear_tracked_data(void *data);
-bool northd_nb_nb_global_handler(struct engine_node *, void *data OVS_UNUSED);
+bool northd_global_config_handler(struct engine_node *, void *data OVS_UNUSED);
 bool northd_nb_logical_switch_handler(struct engine_node *, void *data);
 bool northd_nb_logical_router_handler(struct engine_node *, void *data);
 bool northd_sb_port_binding_handler(struct engine_node *, void *data);
diff --git a/northd/en-sync-sb.c b/northd/en-sync-sb.c
index 733fb9024e..73b30272c4 100644
--- a/northd/en-sync-sb.c
+++ b/northd/en-sync-sb.c
@@ -22,6 +22,7 @@ 
 #include "openvswitch/util.h"
 
 #include "en-lr-nat.h"
+#include "en-global-config.h"
 #include "en-lr-stateful.h"
 #include "en-sync-sb.h"
 #include "lib/inc-proc-eng.h"
@@ -42,7 +43,8 @@  static void sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
                            const struct nbrec_address_set_table *,
                            const struct nbrec_port_group_table *,
                            const struct sbrec_address_set_table *,
-                           const struct lr_stateful_table *);
+                           const struct lr_stateful_table *,
+                           const char *svc_monitor_macp);
 static const struct sbrec_address_set *sb_address_set_lookup_by_name(
     struct ovsdb_idl_index *, const char *name);
 static void update_sb_addr_set(struct sorted_array *,
@@ -90,9 +92,12 @@  en_sync_to_sb_addr_set_run(struct engine_node *node, void *data OVS_UNUSED)
     const struct engine_context *eng_ctx = engine_get_context();
     const struct ed_type_lr_stateful *lr_stateful_data =
         engine_get_input_data("lr_stateful", node);
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
     sync_addr_sets(eng_ctx->ovnsb_idl_txn, nb_address_set_table,
                    nb_port_group_table, sb_address_set_table,
-                   &lr_stateful_data->lr_sful_table);
+                   &lr_stateful_data->lr_sful_table,
+                   global_config->svc_monitor_mac);
 
     engine_set_node_state(node, EN_UPDATED);
 }
@@ -218,12 +223,14 @@  en_sync_to_sb_lb_run(struct engine_node *node, void *data OVS_UNUSED)
 {
     const struct sbrec_load_balancer_table *sb_load_balancer_table =
         EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
+    struct ed_type_global_config *global_config =
+        engine_get_input_data("global_config", node);
     const struct engine_context *eng_ctx = engine_get_context();
     struct northd_data *northd_data = engine_get_input_data("northd", node);
 
     sync_lbs(eng_ctx->ovnsb_idl_txn, sb_load_balancer_table,
              &northd_data->ls_datapaths, &northd_data->lr_datapaths,
-             &northd_data->lb_datapaths_map, &northd_data->features);
+             &northd_data->lb_datapaths_map, &global_config->features);
     engine_set_node_state(node, EN_UPDATED);
 }
 
@@ -369,7 +376,8 @@  sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
                const struct nbrec_address_set_table *nb_address_set_table,
                const struct nbrec_port_group_table *nb_port_group_table,
                const struct sbrec_address_set_table *sb_address_set_table,
-               const struct lr_stateful_table *lr_statefuls)
+               const struct lr_stateful_table *lr_statefuls,
+               const char *svc_monitor_macp)
 {
     struct shash sb_address_sets = SHASH_INITIALIZER(&sb_address_sets);
 
@@ -380,8 +388,10 @@  sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
     }
 
     /* Service monitor MAC. */
-    const char *svc_monitor_macp = northd_get_svc_monitor_mac();
-    struct sorted_array svc = sorted_array_create(&svc_monitor_macp, 1, false);
+    struct sorted_array svc = {
+            .arr = &svc_monitor_macp,
+            .n = 1,
+    };
     sync_addr_set(ovnsb_txn, "svc_monitor_mac", &svc, &sb_address_sets);
     sorted_array_destroy(&svc);
 
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index f7c3d2bcf5..7b1c6597e2 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -30,6 +30,7 @@ 
 #include "openvswitch/poll-loop.h"
 #include "openvswitch/vlog.h"
 #include "inc-proc-northd.h"
+#include "en-global-config.h"
 #include "en-lb-data.h"
 #include "en-lr-stateful.h"
 #include "en-lr-nat.h"
@@ -149,6 +150,7 @@  static ENGINE_NODE(fdb_aging, "fdb_aging");
 static ENGINE_NODE(fdb_aging_waker, "fdb_aging_waker");
 static ENGINE_NODE(sync_to_sb_lb, "sync_to_sb_lb");
 static ENGINE_NODE(sync_to_sb_pb, "sync_to_sb_pb");
+static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(global_config, "global_config");
 static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
 static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lr_nat, "lr_nat");
 static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lr_stateful, "lr_stateful");
@@ -168,11 +170,17 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_lb_data, &en_nb_logical_router,
                      lb_data_logical_router_handler);
 
+    engine_add_input(&en_global_config, &en_nb_nb_global,
+                     global_config_nb_global_handler);
+    engine_add_input(&en_global_config, &en_sb_sb_global,
+                     global_config_sb_global_handler);
+    engine_add_input(&en_global_config, &en_sb_chassis,
+                     global_config_sb_chassis_handler);
+
     engine_add_input(&en_northd, &en_nb_mirror, NULL);
     engine_add_input(&en_northd, &en_nb_static_mac_binding, NULL);
     engine_add_input(&en_northd, &en_nb_chassis_template_var, NULL);
 
-    engine_add_input(&en_northd, &en_sb_sb_global, NULL);
     engine_add_input(&en_northd, &en_sb_chassis, NULL);
     engine_add_input(&en_northd, &en_sb_mirror, NULL);
     engine_add_input(&en_northd, &en_sb_meter, NULL);
@@ -185,11 +193,11 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_northd, &en_sb_fdb, NULL);
     engine_add_input(&en_northd, &en_sb_static_mac_binding, NULL);
     engine_add_input(&en_northd, &en_sb_chassis_template_var, NULL);
+    engine_add_input(&en_northd, &en_global_config,
+                     northd_global_config_handler);
 
     engine_add_input(&en_northd, &en_sb_port_binding,
                      northd_sb_port_binding_handler);
-    engine_add_input(&en_northd, &en_nb_nb_global,
-                     northd_nb_nb_global_handler);
     engine_add_input(&en_northd, &en_nb_logical_switch,
                      northd_nb_logical_switch_handler);
     engine_add_input(&en_northd, &en_nb_logical_router,
@@ -207,15 +215,17 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_ls_stateful, &en_port_group,
                      ls_stateful_port_group_handler);
 
-    engine_add_input(&en_mac_binding_aging, &en_nb_nb_global, NULL);
     engine_add_input(&en_mac_binding_aging, &en_sb_mac_binding, NULL);
     engine_add_input(&en_mac_binding_aging, &en_northd, NULL);
     engine_add_input(&en_mac_binding_aging, &en_mac_binding_aging_waker, NULL);
+    engine_add_input(&en_mac_binding_aging, &en_global_config,
+                     node_global_config_handler);
 
-    engine_add_input(&en_fdb_aging, &en_nb_nb_global, NULL);
     engine_add_input(&en_fdb_aging, &en_sb_fdb, NULL);
     engine_add_input(&en_fdb_aging, &en_northd, NULL);
     engine_add_input(&en_fdb_aging, &en_fdb_aging_waker, NULL);
+    engine_add_input(&en_fdb_aging, &en_global_config,
+                     node_global_config_handler);
 
     engine_add_input(&en_sync_meters, &en_nb_acl, NULL);
     engine_add_input(&en_sync_meters, &en_nb_meter, NULL);
@@ -229,18 +239,22 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_lflow, &en_sb_multicast_group, NULL);
     engine_add_input(&en_lflow, &en_sb_igmp_group, NULL);
     engine_add_input(&en_lflow, &en_sb_logical_dp_group, NULL);
+    engine_add_input(&en_lflow, &en_global_config,
+                     node_global_config_handler);
     engine_add_input(&en_lflow, &en_northd, lflow_northd_handler);
     engine_add_input(&en_lflow, &en_port_group, lflow_port_group_handler);
     engine_add_input(&en_lflow, &en_lr_stateful, lflow_lr_stateful_handler);
     engine_add_input(&en_lflow, &en_ls_stateful, lflow_ls_stateful_handler);
 
+    engine_add_input(&en_sync_to_sb_addr_set, &en_northd, NULL);
+    engine_add_input(&en_sync_to_sb_addr_set, &en_lr_stateful, NULL);
+    engine_add_input(&en_sync_to_sb_addr_set, &en_sb_address_set, NULL);
     engine_add_input(&en_sync_to_sb_addr_set, &en_nb_address_set,
                      sync_to_sb_addr_set_nb_address_set_handler);
     engine_add_input(&en_sync_to_sb_addr_set, &en_nb_port_group,
                      sync_to_sb_addr_set_nb_port_group_handler);
-    engine_add_input(&en_sync_to_sb_addr_set, &en_northd, NULL);
-    engine_add_input(&en_sync_to_sb_addr_set, &en_lr_stateful, NULL);
-    engine_add_input(&en_sync_to_sb_addr_set, &en_sb_address_set, NULL);
+    engine_add_input(&en_sync_to_sb_addr_set, &en_global_config,
+                     node_global_config_handler);
 
     engine_add_input(&en_port_group, &en_nb_port_group,
                      port_group_nb_port_group_handler);
@@ -250,6 +264,8 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
      * table too (because of the explicit dependency in the schema). */
     engine_add_input(&en_port_group, &en_northd, engine_noop_handler);
 
+    engine_add_input(&en_sync_to_sb_lb, &en_global_config,
+                     node_global_config_handler);
     engine_add_input(&en_sync_to_sb_lb, &en_northd,
                      sync_to_sb_lb_northd_handler);
     engine_add_input(&en_sync_to_sb_lb, &en_sb_load_balancer,
@@ -354,11 +370,11 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
                                 "sbrec_fdb_by_dp_and_port",
                                 sbrec_fdb_by_dp_and_port);
 
-    struct northd_data *northd_data =
-        engine_get_internal_data(&en_northd);
+    struct ed_type_global_config *global_config =
+        engine_get_internal_data(&en_global_config);
     unixctl_command_register("debug/chassis-features-list", "", 0, 0,
                              chassis_features_list,
-                             &northd_data->features);
+                             &global_config->features);
 }
 
 /* Returns true if the incremental processing ended up updating nodes. */
diff --git a/northd/northd.c b/northd/northd.c
index 59b30c5dc8..e1eda9fba5 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -43,6 +43,7 @@ 
 #include "lflow-mgr.h"
 #include "memory.h"
 #include "northd.h"
+#include "en-global-config.h"
 #include "en-lb-data.h"
 #include "en-lr-nat.h"
 #include "en-lr-stateful.h"
@@ -77,14 +78,6 @@  static bool install_ls_lb_from_router;
 /* Use common zone for SNAT and DNAT if this option is set to "true". */
 static bool use_common_zone = false;
 
-/* MAC allocated for service monitor usage. Just one mac is allocatedg5534
- * for this purpose and ovn-controller's on each chassis will make use
- * of this mac when sending out the packets to monitor the services
- * defined in Service_Monitor Southbound table. Since these packets
- * all locally handled, having just one mac is good enough. */
-static char svc_monitor_mac[ETH_ADDR_STRLEN + 1];
-static struct eth_addr svc_monitor_mac_ea;
-
 /* If this option is 'true' northd will make use of ct.inv match fields.
  * Otherwise, it will avoid using it.  The default is true. */
 static bool use_ct_inv_match = true;
@@ -295,66 +288,6 @@  ovn_stage_to_datapath_type(enum ovn_stage stage)
     }
 }
 
-static void
-build_chassis_features(const struct sbrec_chassis_table *sbrec_chassis_table,
-                       struct chassis_features *chassis_features)
-{
-    const struct sbrec_chassis *chassis;
-
-    SBREC_CHASSIS_TABLE_FOR_EACH (chassis, sbrec_chassis_table) {
-        /* Only consider local AZ chassis.  Remote ones don't install
-         * flows generated by the local northd.
-         */
-        if (smap_get_bool(&chassis->other_config, "is-remote", false)) {
-            continue;
-        }
-
-        bool ct_no_masked_label =
-            smap_get_bool(&chassis->other_config,
-                          OVN_FEATURE_CT_NO_MASKED_LABEL,
-                          false);
-        if (!ct_no_masked_label && chassis_features->ct_no_masked_label) {
-            chassis_features->ct_no_masked_label = false;
-        }
-
-        bool mac_binding_timestamp =
-            smap_get_bool(&chassis->other_config,
-                          OVN_FEATURE_MAC_BINDING_TIMESTAMP,
-                          false);
-        if (!mac_binding_timestamp &&
-            chassis_features->mac_binding_timestamp) {
-            chassis_features->mac_binding_timestamp = false;
-        }
-
-        bool ct_lb_related =
-            smap_get_bool(&chassis->other_config,
-                          OVN_FEATURE_CT_LB_RELATED,
-                          false);
-        if (!ct_lb_related &&
-            chassis_features->ct_lb_related) {
-            chassis_features->ct_lb_related = false;
-        }
-
-        bool fdb_timestamp =
-            smap_get_bool(&chassis->other_config,
-                          OVN_FEATURE_FDB_TIMESTAMP,
-                          false);
-        if (!fdb_timestamp &&
-            chassis_features->fdb_timestamp) {
-            chassis_features->fdb_timestamp = false;
-        }
-
-        bool ls_dpg_column =
-            smap_get_bool(&chassis->other_config,
-                          OVN_FEATURE_LS_DPG_COLUMN,
-                          false);
-        if (!ls_dpg_column &&
-            chassis_features->ls_dpg_column) {
-            chassis_features->ls_dpg_column = false;
-        }
-    }
-}
-
 static uint32_t
 allocate_queueid(unsigned long *queue_id_bitmap)
 {
@@ -946,7 +879,7 @@  is_vxlan_mode(const struct sbrec_chassis_table *sbrec_chassis_table)
     return false;
 }
 
-static uint32_t
+uint32_t
 get_ovn_max_dp_key_local(const struct sbrec_chassis_table *sbrec_chassis_table)
 {
     if (is_vxlan_mode(sbrec_chassis_table)) {
@@ -3354,6 +3287,8 @@  create_or_get_service_mon(struct ovsdb_idl_txn *ovnsb_txn,
 static void
 ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
                   const struct ovn_northd_lb *lb,
+                  const char *svc_monitor_mac,
+                  const struct eth_addr *svc_monitor_mac_ea,
                   struct hmap *monitor_map, struct hmap *ls_ports,
                   struct sset *svc_monitor_lsps)
 {
@@ -3399,7 +3334,7 @@  ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
             struct eth_addr ea;
             if (!mon_info->sbrec_mon->src_mac ||
                 !eth_addr_from_string(mon_info->sbrec_mon->src_mac, &ea) ||
-                !eth_addr_equals(ea, svc_monitor_mac_ea)) {
+                !eth_addr_equals(ea, *svc_monitor_mac_ea)) {
                 sbrec_service_monitor_set_src_mac(mon_info->sbrec_mon,
                                                   svc_monitor_mac);
             }
@@ -3724,6 +3659,8 @@  static void
 build_lb_svcs(
     struct ovsdb_idl_txn *ovnsb_txn,
     const struct sbrec_service_monitor_table *sbrec_service_monitor_table,
+    const char *svc_monitor_mac,
+    const struct eth_addr *svc_monitor_mac_ea,
     struct hmap *ls_ports, struct hmap *lb_dps_map,
     struct sset *svc_monitor_lsps,
     struct hmap *svc_monitor_map)
@@ -3742,7 +3679,8 @@  build_lb_svcs(
 
     struct ovn_lb_datapaths *lb_dps;
     HMAP_FOR_EACH (lb_dps, hmap_node, lb_dps_map) {
-        ovn_lb_svc_create(ovnsb_txn, lb_dps->lb, svc_monitor_map, ls_ports,
+        ovn_lb_svc_create(ovnsb_txn, lb_dps->lb, svc_monitor_mac,
+                          svc_monitor_mac_ea, svc_monitor_map, ls_ports,
                           svc_monitor_lsps);
     }
 
@@ -3834,13 +3772,16 @@  static void
 build_lb_port_related_data(
     struct ovsdb_idl_txn *ovnsb_txn,
     const struct sbrec_service_monitor_table *sbrec_service_monitor_table,
+    const char *svc_monitor_mac,
+    const struct eth_addr *svc_monitor_mac_ea,
     struct ovn_datapaths *lr_datapaths, struct hmap *ls_ports,
     struct hmap *lb_dps_map, struct hmap *lb_group_dps_map,
     struct sset *svc_monitor_lsps,
     struct hmap *svc_monitor_map)
 {
     build_lrouter_lbs_check(lr_datapaths);
-    build_lb_svcs(ovnsb_txn, sbrec_service_monitor_table, ls_ports, lb_dps_map,
+    build_lb_svcs(ovnsb_txn, sbrec_service_monitor_table, svc_monitor_mac,
+                  svc_monitor_mac_ea, ls_ports, lb_dps_map,
                   svc_monitor_lsps, svc_monitor_map);
     build_lswitch_lbs_from_lrouter(lr_datapaths, lb_dps_map, lb_group_dps_map);
 }
@@ -9378,6 +9319,7 @@  build_lswitch_arp_nd_responder_default(struct ovn_datapath *od,
 static void
 build_lswitch_arp_nd_service_monitor(const struct ovn_northd_lb *lb,
                                      const struct hmap *ls_ports,
+                                     const char *svc_monitor_mac,
                                      struct lflow_table *lflows,
                                      struct ds *actions,
                                      struct ds *match,
@@ -15783,6 +15725,7 @@  struct lswitch_flow_build_info {
     struct ds match;
     struct ds actions;
     size_t thread_lflow_counter;
+    const char *svc_monitor_mac;
 };
 
 /* Helper function to combine all lflow generation which is iterated by
@@ -16010,6 +15953,7 @@  build_lflows_thread(void *arg)
                     }
                     build_lswitch_arp_nd_service_monitor(lb_dps->lb,
                                                          lsi->ls_ports,
+                                                         lsi->svc_monitor_mac,
                                                          lsi->lflows,
                                                          &lsi->match,
                                                          &lsi->actions,
@@ -16133,7 +16077,8 @@  build_lswitch_and_lrouter_flows(const struct ovn_datapaths *ls_datapaths,
                                 const struct hmap *lb_dps_map,
                                 const struct hmap *svc_monitor_map,
                                 const struct hmap *bfd_connections,
-                                const struct chassis_features *features)
+                                const struct chassis_features *features,
+                                const char *svc_monitor_mac)
 {
 
     char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
@@ -16166,6 +16111,7 @@  build_lswitch_and_lrouter_flows(const struct ovn_datapaths *ls_datapaths,
             lsiv[index].features = features;
             lsiv[index].svc_check_match = svc_check_match;
             lsiv[index].thread_lflow_counter = 0;
+            lsiv[index].svc_monitor_mac = svc_monitor_mac;
             ds_init(&lsiv[index].match);
             ds_init(&lsiv[index].actions);
 
@@ -16205,6 +16151,7 @@  build_lswitch_and_lrouter_flows(const struct ovn_datapaths *ls_datapaths,
             .bfd_connections = bfd_connections,
             .features = features,
             .svc_check_match = svc_check_match,
+            .svc_monitor_mac = svc_monitor_mac,
             .match = DS_EMPTY_INITIALIZER,
             .actions = DS_EMPTY_INITIALIZER,
         };
@@ -16247,6 +16194,7 @@  build_lswitch_and_lrouter_flows(const struct ovn_datapaths *ls_datapaths,
         stopwatch_start(LFLOWS_LBS_STOPWATCH_NAME, time_msec());
         HMAP_FOR_EACH (lb_dps, hmap_node, lb_dps_map) {
             build_lswitch_arp_nd_service_monitor(lb_dps->lb, lsi.ls_ports,
+                                                 lsi.svc_monitor_mac,
                                                  lsi.lflows, &lsi.actions,
                                                  &lsi.match,
                                                  lb_dps->lflow_ref);
@@ -16365,7 +16313,8 @@  void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
                                     input_data->lb_datapaths_map,
                                     input_data->svc_monitor_map,
                                     input_data->bfd_connections,
-                                    input_data->features);
+                                    input_data->features,
+                                    input_data->svc_monitor_mac);
 
     if (parallelization_state == STATE_INIT_HASH_SIZES) {
         parallelization_state = STATE_USE_PARALLELIZATION;
@@ -16634,6 +16583,7 @@  lflow_handle_northd_lb_changes(struct ovsdb_idl_txn *ovnsb_txn,
         struct ds actions = DS_EMPTY_INITIALIZER;
 
         build_lswitch_arp_nd_service_monitor(lb_dps->lb, lflow_input->ls_ports,
+                                             lflow_input->svc_monitor_mac,
                                              lflows, &actions,
                                              &match, lb_dps->lflow_ref);
         build_lrouter_defrag_flows_for_lb(lb_dps, lflows,
@@ -17411,18 +17361,6 @@  destroy_datapaths_and_ports(struct ovn_datapaths *ls_datapaths,
     ovn_datapaths_destroy(lr_datapaths);
 }
 
-static void
-northd_enable_all_features(struct northd_data *data)
-{
-    data->features = (struct chassis_features) {
-        .ct_no_masked_label = true,
-        .mac_binding_timestamp = true,
-        .ct_lb_related = true,
-        .fdb_timestamp = true,
-        .ls_dpg_column = true,
-    };
-}
-
 void
 northd_init(struct northd_data *data)
 {
@@ -17433,8 +17371,6 @@  northd_init(struct northd_data *data)
     hmap_init(&data->lb_datapaths_map);
     hmap_init(&data->lb_group_datapaths_map);
     ovs_list_init(&data->lr_list);
-    northd_enable_all_features(data);
-    data->ovn_internal_version_changed = false;
     sset_init(&data->svc_monitor_lsps);
     hmap_init(&data->svc_monitor_map);
     init_northd_tracked_data(data);
@@ -17474,7 +17410,6 @@  northd_destroy(struct northd_data *data)
     destroy_datapaths_and_ports(&data->ls_datapaths, &data->lr_datapaths,
                                 &data->ls_ports, &data->lr_ports,
                                 &data->lr_list);
-    destroy_debug_config();
 
     sset_destroy(&data->svc_monitor_lsps);
     destroy_northd_tracked_data(data);
@@ -17491,83 +17426,22 @@  ovnnb_db_run(struct northd_input *input_data,
     }
     stopwatch_start(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
 
-    /* Sync ipsec configuration.
-     * Copy nb_cfg from northbound to southbound database.
-     * Also set up to update sb_cfg once our southbound transaction commits. */
-    const struct nbrec_nb_global *nb = nbrec_nb_global_table_first(
-                                       input_data->nbrec_nb_global_table);
-    if (!nb) {
-        nb = nbrec_nb_global_insert(ovnnb_txn);
-    }
-
-    const char *mac_addr_prefix = set_mac_prefix(smap_get(&nb->options,
-                                                          "mac_prefix"));
-
-    const char *monitor_mac = smap_get(&nb->options, "svc_monitor_mac");
-    if (monitor_mac) {
-        if (eth_addr_from_string(monitor_mac, &svc_monitor_mac_ea)) {
-            snprintf(svc_monitor_mac, sizeof svc_monitor_mac,
-                     ETH_ADDR_FMT, ETH_ADDR_ARGS(svc_monitor_mac_ea));
-        } else {
-            monitor_mac = NULL;
-        }
-    }
-
-    struct smap options;
-    smap_clone(&options, &nb->options);
-
-    smap_replace(&options, "mac_prefix", mac_addr_prefix);
-
-    if (!monitor_mac) {
-        eth_addr_random(&svc_monitor_mac_ea);
-        snprintf(svc_monitor_mac, sizeof svc_monitor_mac,
-                 ETH_ADDR_FMT, ETH_ADDR_ARGS(svc_monitor_mac_ea));
-        smap_replace(&options, "svc_monitor_mac", svc_monitor_mac);
-    }
-
-    char *max_tunid = xasprintf("%d",
-        get_ovn_max_dp_key_local(input_data->sbrec_chassis_table));
-    smap_replace(&options, "max_tunid", max_tunid);
-    free(max_tunid);
-
-    char *ovn_internal_version = ovn_get_internal_version();
-    if (!strcmp(ovn_internal_version,
-                smap_get_def(&options, "northd_internal_version", ""))) {
-        data->ovn_internal_version_changed = false;
-    } else {
-        smap_replace(&options, "northd_internal_version",
-                     ovn_internal_version);
-    }
-    free(ovn_internal_version);
-
-    if (!smap_equal(&nb->options, &options)) {
-        nbrec_nb_global_verify_options(nb);
-        nbrec_nb_global_set_options(nb, &options);
-    }
-
-    use_ct_inv_match = smap_get_bool(&nb->options,
+    use_ct_inv_match = smap_get_bool(input_data->nb_options,
                                      "use_ct_inv_match", true);
 
     /* deprecated, use --event instead */
-    controller_event_en = smap_get_bool(&nb->options,
+    controller_event_en = smap_get_bool(input_data->nb_options,
                                         "controller_event", false);
-    check_lsp_is_up = !smap_get_bool(&nb->options,
+    check_lsp_is_up = !smap_get_bool(input_data->nb_options,
                                      "ignore_lsp_down", true);
-    default_acl_drop = smap_get_bool(&nb->options, "default_acl_drop", false);
+    default_acl_drop = smap_get_bool(input_data->nb_options,
+                                     "default_acl_drop", false);
 
-    install_ls_lb_from_router = smap_get_bool(&nb->options,
+    install_ls_lb_from_router = smap_get_bool(input_data->nb_options,
                                               "install_ls_lb_from_router",
                                               false);
-    use_common_zone = smap_get_bool(&nb->options, "use_common_zone", false);
-
-    if (smap_get_bool(&nb->options, "ignore_chassis_features", false)) {
-        northd_enable_all_features(data);
-    } else {
-        build_chassis_features(input_data->sbrec_chassis_table,
-                               &data->features);
-    }
-
-    init_debug_config(nb);
+    use_common_zone = smap_get_bool(input_data->nb_options, "use_common_zone",
+                                    false);
 
     build_datapaths(ovnsb_txn,
                     input_data->nbrec_logical_switch_table,
@@ -17592,6 +17466,8 @@  ovnnb_db_run(struct northd_input *input_data,
                 &data->ls_ports, &data->lr_ports);
     build_lb_port_related_data(ovnsb_txn,
                                input_data->sbrec_service_monitor_table,
+                               input_data->svc_monitor_mac,
+                               &input_data->svc_monitor_mac_ea,
                                &data->lr_datapaths, &data->ls_ports,
                                &data->lb_datapaths_map,
                                &data->lb_group_datapaths_map,
@@ -17624,38 +17500,6 @@  ovnnb_db_run(struct northd_input *input_data,
                               &data->ls_datapaths.datapaths);
     stopwatch_stop(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
 
-    /* Set up SB_Global (depends on chassis features). */
-    const struct sbrec_sb_global *sb = sbrec_sb_global_table_first(
-                                       input_data->sbrec_sb_global_table);
-    if (!sb) {
-        sb = sbrec_sb_global_insert(ovnsb_txn);
-    }
-    if (nb->ipsec != sb->ipsec) {
-        sbrec_sb_global_set_ipsec(sb, nb->ipsec);
-    }
-
-    /* Inform ovn-controllers whether LB flows will use ct_mark (i.e., only
-     * if all chassis support it).  If not explicitly present in the database
-     * the default value to be used for this option is 'true'.
-     */
-    if (!data->features.ct_no_masked_label) {
-        smap_replace(&options, "lb_hairpin_use_ct_mark", "false");
-    } else {
-        smap_remove(&options, "lb_hairpin_use_ct_mark");
-    }
-
-    /* Hackaround SB_global.options overwrite by NB_Global.options for
-     * 'sbctl_probe_interval' option.
-     */
-    const char *sip = smap_get(&sb->options, "sbctl_probe_interval");
-    if (sip) {
-        smap_replace(&options, "sbctl_probe_interval", sip);
-    }
-
-    if (!smap_equal(&sb->options, &options)) {
-        sbrec_sb_global_set_options(sb, &options);
-    }
-    smap_destroy(&options);
 }
 
 /* Stores the set of chassis which references an ha_chassis_group.
@@ -17946,12 +17790,6 @@  ovnsb_db_run(struct ovsdb_idl_txn *ovnnb_txn,
     ovn_update_ipv6_prefix(lr_ports);
 }
 
-const char *
-northd_get_svc_monitor_mac(void)
-{
-    return svc_monitor_mac;
-}
-
 const struct ovn_datapath *
 northd_get_datapath_for_port(const struct hmap *ls_ports,
                              const char *port_name)
diff --git a/northd/northd.h b/northd/northd.h
index c665092a09..7e48bb966d 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -27,7 +27,6 @@ 
 
 struct northd_input {
     /* Northbound table references */
-    const struct nbrec_nb_global_table *nbrec_nb_global_table;
     const struct nbrec_logical_switch_table *nbrec_logical_switch_table;
     const struct nbrec_logical_router_table *nbrec_logical_router_table;
     const struct nbrec_static_mac_binding_table
@@ -37,7 +36,6 @@  struct northd_input {
     const struct nbrec_mirror_table *nbrec_mirror_table;
 
     /* Southbound table references */
-    const struct sbrec_sb_global_table *sbrec_sb_global_table;
     const struct sbrec_datapath_binding_table *sbrec_datapath_binding_table;
     const struct sbrec_port_binding_table *sbrec_port_binding_table;
     const struct sbrec_mac_binding_table *sbrec_mac_binding_table;
@@ -57,6 +55,13 @@  struct northd_input {
     const struct hmap *lbs;
     const struct hmap *lbgrps;
 
+    /* Global config data node inputs. */
+    const struct smap *nb_options;
+    const struct smap *sb_options;
+    const char *svc_monitor_mac;
+    struct eth_addr svc_monitor_mac_ea;
+    const struct chassis_features *features;
+
     /* Indexes */
     struct ovsdb_idl_index *sbrec_chassis_by_name;
     struct ovsdb_idl_index *sbrec_chassis_by_hostname;
@@ -66,14 +71,6 @@  struct northd_input {
     struct ovsdb_idl_index *sbrec_fdb_by_dp_and_port;
 };
 
-struct chassis_features {
-    bool ct_no_masked_label;
-    bool mac_binding_timestamp;
-    bool ct_lb_related;
-    bool fdb_timestamp;
-    bool ls_dpg_column;
-};
-
 /* A collection of datapaths. E.g. all logical switch datapaths, or all
  * logical router datapaths. */
 struct ovn_datapaths {
@@ -208,8 +205,6 @@  struct northd_data {
     struct hmap lb_datapaths_map;
     struct hmap lb_group_datapaths_map;
     struct ovs_list lr_list;
-    bool ovn_internal_version_changed;
-    struct chassis_features features;
     struct sset svc_monitor_lsps;
     struct hmap svc_monitor_map;
 
@@ -246,6 +241,7 @@  struct lflow_input {
     const struct chassis_features *features;
     const struct hmap *svc_monitor_map;
     bool ovn_internal_version_changed;
+    const char *svc_monitor_mac;
 };
 
 extern int parallelization_state;
@@ -764,8 +760,6 @@  void bfd_cleanup_connections(const struct nbrec_bfd_table *,
                              struct hmap *bfd_map);
 void run_update_worker_pool(int n_threads);
 
-const char *northd_get_svc_monitor_mac(void);
-
 const struct ovn_datapath *northd_get_datapath_for_port(
     const struct hmap *ls_ports, const char *port_name);
 void sync_lbs(struct ovsdb_idl_txn *, const struct sbrec_load_balancer_table *,
@@ -829,4 +823,6 @@  bool lrouter_port_ipv4_reachable(const struct ovn_port *, ovs_be32 addr);
 bool lrouter_port_ipv6_reachable(const struct ovn_port *,
                                  const struct in6_addr *);
 
+uint32_t get_ovn_max_dp_key_local(const struct sbrec_chassis_table *);
+
 #endif /* NORTHD_H */
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 9b2886493c..03d525bdc2 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -8704,7 +8704,7 @@  AT_CHECK([grep "ls_in_lb " S1flows | sed 's/table=../table=??/' | sort], [0], [d
   table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
 ])
 
-ovn-nbctl --wait=sb set NB_Global . options:install_ls_lb_from_router=true
+check ovn-nbctl --wait=sb set NB_Global . options:install_ls_lb_from_router=true
 
 ovn-sbctl dump-flows S0 > S0flows
 ovn-sbctl dump-flows S1 > S1flows
@@ -8723,6 +8723,7 @@  AT_CHECK([grep "ls_in_lb " S1flows | sed 's/table=../table=??/' | sort], [0], [d
   table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 172.16.0.11 && tcp.dst == 8080), action=(reg0[[1]] = 0; ct_lb_mark(backends=10.0.0.2:8080);)
 ])
 
+
 ovn-sbctl get datapath S0 _uuid > dp_uuids
 ovn-sbctl get datapath S1 _uuid >> dp_uuids
 lb_dp_group=$(ovn-sbctl --bare --columns ls_datapath_group find Load_Balancer name=lb0)
@@ -8731,7 +8732,7 @@  AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Gr
 $(cat dp_uuids | sort)
 ])
 
-ovn-nbctl --wait=sb set NB_Global . options:install_ls_lb_from_router=false
+check ovn-nbctl --wait=sb set NB_Global . options:install_ls_lb_from_router=false
 
 ovn-sbctl dump-flows S0 > S0flows
 ovn-sbctl dump-flows S1 > S1flows
@@ -9088,12 +9089,11 @@  $4
 AS_BOX([Create new PG1 and PG2])
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
 check ovn-nbctl --wait=sb -- pg-add pg1 -- pg-add pg2
-dnl The northd node should not recompute, it should handle nb_global update
-dnl though, therefore "compute: 1".
+dnl The northd node should not recompute.
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl The port_group node recomputes every time a NB port group is added/deleted.
@@ -9126,12 +9126,11 @@  check ovn-nbctl --wait=sb         \
 check_column "sw1.1" sb:Port_Group ports name="${sw1_key}_pg1"
 check_column "sw2.1" sb:Port_Group ports name="${sw2_key}_pg1"
 
-dnl The northd node should not recompute, it should handle nb_global update
-dnl though, therefore "compute: 1".
+dnl The northd node should not recompute.
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl The port_group node recomputes also every time a port from a new switch
@@ -9163,12 +9162,11 @@  check_column "sw2.1" sb:Port_Group ports name="${sw2_key}_pg1"
 check_column "sw1.2" sb:Port_Group ports name="${sw1_key}_pg2"
 check_column "sw2.2" sb:Port_Group ports name="${sw2_key}_pg2"
 
-dnl The northd node should not recompute, it should handle nb_global update
-dnl though, therefore "compute: 1".
+dnl The northd node should not recompute.
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl The port_group node recomputes also every time a port from a new switch
@@ -9201,12 +9199,11 @@  check_column "sw2.1 sw2.3" sb:Port_Group ports name="${sw2_key}_pg1"
 check_column "sw1.2 sw1.3" sb:Port_Group ports name="${sw1_key}_pg2"
 check_column "sw2.2 sw2.3" sb:Port_Group ports name="${sw2_key}_pg2"
 
-dnl The northd node should not recompute, it should handle nb_global update
-dnl though, therefore "compute: 1".
+dnl The northd node should not recompute.
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl We did not change the set of switches a pg is applied to, there should be
@@ -9244,7 +9241,7 @@  dnl though, therefore "compute: 1".
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl We did not change the set of switches a pg is applied to, there should be
@@ -9276,12 +9273,11 @@  check_column "sw1.2" sb:Port_Group ports name="${sw1_key}_pg2"
 AT_CHECK([fetch_column sb:Port_Group ports name="${sw2_key}_pg2"], [0], [
 ])
 
-dnl The northd node should not recompute, it should handle nb_global update
-dnl though, therefore "compute: 1".
+dnl The northd node should not recompute.
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl We changed the set of switches a pg is applied to, there should be
@@ -9314,12 +9310,11 @@  check_column "sw1.2" sb:Port_Group ports name="${sw1_key}_pg2"
 AT_CHECK([fetch_column sb:Port_Group ports name="${sw2_key}_pg2"], [0], [
 ])
 
-dnl The northd node should not recompute, it should handle nb_global update
-dnl though, therefore "compute: 1".
+dnl The northd node should not recompute.
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl We changed the set of switches a pg is applied to, there should be
@@ -9352,12 +9347,11 @@  check_column "sw2.1" sb:Port_Group ports name="${sw2_key}_pg1"
 check_column "sw1.2" sb:Port_Group ports name="${sw1_key}_pg2"
 check_column "sw2.2" sb:Port_Group ports name="${sw2_key}_pg2"
 
-dnl The northd node should not recompute, it should handle nb_global update
-dnl though, therefore "compute: 1".
+dnl The northd node should not recompute.
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl We changed the set of switches a pg is applied to, there should be a
@@ -9392,12 +9386,11 @@  check_column "sw1.2" sb:Port_Group ports name="${sw1_key}_pg2"
 AT_CHECK([fetch_column sb:Port_Group ports name="${sw2_key}_pg2"], [0], [
 ])
 
-dnl The northd node should not recompute, it should handle nb_global update
-dnl though, therefore "compute: 1".
+dnl The northd node should not recompute,.
 AT_CHECK([as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats northd], [0], [dnl
 Node: northd
 - recompute:            0
-- compute:              1
+- compute:              0
 - abort:                0
 ])
 dnl We changed the set of switches a pg is applied to, there should be a
@@ -11343,3 +11336,212 @@  CHECK_NO_CHANGE_AFTER_RECOMPUTE
 OVN_CLEANUP([hv1])
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([NB_Global and SB_Global incremental processing])
+
+ovn_start
+
+check_engine_stats() {
+  node=$1
+  recompute=$2
+  compute=$3
+
+  echo "__file__:__line__: Checking engine stats for node $node : recompute - \
+$recompute : compute - $compute"
+
+  node_stat=$(as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats $node)
+  # node_stat will be of this format :
+  #     - Node: lflow - recompute: 3 - compute: 0 - abort: 0
+  node_recompute_ct=$(echo $node_stat | cut -d '-' -f2 | cut -d ':' -f2)
+  node_compute_ct=$(echo $node_stat | cut -d '-' -f3 | cut -d ':' -f2)
+
+  if [[ "$recompute" == "norecompute" ]]; then
+    # node should not be recomputed
+    echo "Expecting $node recompute count - $node_recompute_ct to be 0"
+    check test "$node_recompute_ct" -eq "0"
+  else
+    echo "Expecting $node recompute count - $node_recompute_ct not to be 0"
+    check test "$node_recompute_ct" -ne "0"
+  fi
+
+  if [[ "$compute" == "nocompute" ]]; then
+    # node should not be computed
+    echo "Expecting $node compute count - $node_compute_ct to be 0"
+    check test "$node_compute_ct" -eq "0"
+  else
+    echo "Expecting $node compute count - $node_compute_ct not to be 0"
+    check test "$node_compute_ct" -ne "0"
+  fi
+}
+
+check ovn-nbctl ls-add sw0
+check ovn-nbctl lr-add lr0
+check ovn-nbctl lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1 "00:00:20:20:00:03 10.0.0.3"
+check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:20:20:12:14 10.0.0.1/24
+check ovn-nbctl lsp-add sw0 sw0-lr0
+check ovn-nbctl lsp-set-type sw0-lr0 router
+check ovn-nbctl lsp-set-addresses sw0-lr0 router
+check ovn-nbctl --wait=sb lsp-set-options sw0-lr0 router-port=lr0-sw0
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+
+# This should not result in recomputes.
+check ovn-nbctl --wait=sb set NB_Global . options:foo=bar
+check_engine_stats global_config norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow norecompute compute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+# This should result in recomputes.
+check ovn-sbctl set SB_Global . options:bar=foo
+check_engine_stats global_config recompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Clears an nb option and checks that recomputes were triggered
+# and the option was added back by ovn-northd or not depending
+# on the 'added_back' argument.
+clear_nb_option() {
+  option=$1
+  add_back=$2
+  echo "clearing the nb option - $option"
+  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+  check ovn-nbctl --wait=sb remove NB_Global . options $option
+  check_engine_stats global_config recompute compute
+  check_engine_stats northd recompute nocompute
+  check_engine_stats lflow recompute nocompute
+
+  local retval=1
+  if [ "$add_back" == "true" ]; then
+    retval=0
+  fi
+  AT_CHECK([ovn-nbctl get NB_Global . options:$option], [$retval], [ignore], [ignore])
+}
+
+# Clear svc_monitor_mac and few other options which result in recompute.
+# and ovn-northd should update the nb options back.
+clear_nb_option svc_monitor_mac true
+clear_nb_option max_tunid true
+clear_nb_option mac_prefix true
+clear_nb_option northd_internal_version true
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb set NB_Global . options:ignore_chassis_features=true
+check_engine_stats global_config recompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+clear_nb_option ignore_chassis_features false
+
+set_nb_option_lflow_recompute() {
+  local option=$1
+  local value=$2
+  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+  check ovn-nbctl --wait=sb set NB_Global . options:$option=$value
+  check_engine_stats global_config norecompute compute
+  check_engine_stats northd recompute nocompute
+  check_engine_stats lflow recompute nocompute
+  check_engine_stats mac_binding_aging recompute nocompute
+  CHECK_NO_CHANGE_AFTER_RECOMPUTE
+}
+
+clear_nb_option_lflow_recompute() {
+  local option=$1
+  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+  check ovn-nbctl --wait=sb remove NB_Global . options $option
+  check_engine_stats global_config norecompute compute
+  check_engine_stats northd recompute nocompute
+  check_engine_stats lflow recompute nocompute
+  check_engine_stats mac_binding_aging recompute nocompute
+  CHECK_NO_CHANGE_AFTER_RECOMPUTE
+}
+
+set_nb_option_lflow_recompute debug_drop_domain_id 1
+clear_nb_option_lflow_recompute debug_drop_domain_id
+
+set_nb_option_lflow_recompute debug_drop_collector_set 1
+clear_nb_option_lflow_recompute debug_drop_collector_set
+
+set_nb_option_lflow_recompute mac_binding_removal_limit 100
+clear_nb_option_lflow_recompute mac_binding_removal_limit
+
+set_nb_option_lflow_recompute fdb_removal_limit 100
+clear_nb_option_lflow_recompute fdb_removal_limit
+
+set_nb_option_lflow_recompute controller_event true
+clear_nb_option_lflow_recompute controller_event
+
+set_nb_option_lflow_recompute ignore_lsp_down true
+clear_nb_option_lflow_recompute ignore_lsp_down
+
+set_nb_option_lflow_recompute use_ct_inv_match true
+clear_nb_option_lflow_recompute use_ct_inv_match
+
+set_nb_option_lflow_recompute default_acl_drop true
+clear_nb_option_lflow_recompute default_acl_drop
+
+set_nb_option_lflow_recompute use_common_zone true
+clear_nb_option_lflow_recompute use_common_zone
+
+set_nb_option_lflow_recompute install_ls_lb_from_router true
+clear_nb_option_lflow_recompute install_ls_lb_from_router
+
+# Now test changes to chassis for feature changes.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-sbctl chassis-add ch1 geneve 127.0.0.1
+check ovn-nbctl --wait=sb sync
+check_engine_stats global_config recompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-sbctl chassis-add ch2 geneve 127.0.0.2
+check ovn-nbctl --wait=sb sync
+check_engine_stats global_config recompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+AT_CHECK([ovn-nbctl get NB_Global . options:max_tunid | \
+sed s/":"//g | sed s/\"//g], [0], [16711680
+], [])
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-sbctl chassis-del ch2
+check ovn-nbctl --wait=sb sync
+check_engine_stats global_config recompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-sbctl set encap . type=vxlan
+check ovn-nbctl --wait=sb sync
+check_engine_stats global_config recompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+AT_CHECK([ovn-nbctl get NB_Global . options:max_tunid | \
+sed s/":"//g | sed s/\"//g], [0], [4095
+], [])
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-sbctl set chassis . other_config:foo=bar
+check ovn-nbctl --wait=sb sync
+check_engine_stats global_config norecompute compute
+check_engine_stats mac_binding_aging recompute nocompute
+check_engine_stats fdb_aging recompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-sbctl set chassis . other_config:ct-no-masked-label=true
+check ovn-nbctl --wait=sb sync
+check_engine_stats global_config norecompute compute
+check_engine_stats mac_binding_aging recompute nocompute
+check_engine_stats fdb_aging recompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+AT_CLEANUP
+])