diff --git a/SOURCES/openvswitch-2.16.0.patch b/SOURCES/openvswitch-2.16.0.patch
index 2691b90..8743d43 100644
--- a/SOURCES/openvswitch-2.16.0.patch
+++ b/SOURCES/openvswitch-2.16.0.patch
@@ -1872,7 +1872,7 @@ index a782d9678a..ac4885538c 100644
  
      /* Flow-Table and classifiers
 diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
-index bddce75b63..c56eb184d7 100644
+index bddce75b63..f96d0ecf54 100644
 --- a/lib/dpif-netdev.c
 +++ b/lib/dpif-netdev.c
 @@ -984,7 +984,9 @@ dpif_netdev_subtable_lookup_set(struct unixctl_conn *conn, int argc OVS_UNUSED,
@@ -2015,7 +2015,75 @@ index bddce75b63..c56eb184d7 100644
      return 0;
  }
  
-@@ -8942,9 +8979,12 @@ dpcls_create_subtable(struct dpcls *cls, const struct netdev_flow_key *mask)
+@@ -5033,23 +5070,28 @@ sched_numa_list_put_in_place(struct sched_numa_list *numa_list)
+     }
+ }
+ 
++/* Returns 'true' if OVS rxq scheduling algorithm assigned any unpinned rxq to
++ * a PMD thread core on a non-local numa node. */
+ static bool
+ sched_numa_list_cross_numa_polling(struct sched_numa_list *numa_list)
+ {
+     struct sched_numa *numa;
+ 
+-    /* For each numa */
+     HMAP_FOR_EACH (numa, node, &numa_list->numas) {
+-        /* For each pmd */
+         for (int i = 0; i < numa->n_pmds; i++) {
+             struct sched_pmd *sched_pmd;
+ 
+             sched_pmd = &numa->pmds[i];
+-            /* For each rxq. */
++            if (sched_pmd->isolated) {
++                /* All rxqs on this PMD thread core are pinned. */
++                continue;
++            }
+             for (unsigned k = 0; k < sched_pmd->n_rxq; k++) {
+                 struct dp_netdev_rxq *rxq = sched_pmd->rxqs[k];
+-
+-                if (!sched_pmd->isolated &&
++                /* Check if the rxq is not pinned to a specific PMD thread core
++                 * by the user AND the PMD thread core that OVS assigned is
++                 * non-local to the rxq port. */
++                if (rxq->core_id == OVS_CORE_UNSPEC &&
+                     rxq->pmd->numa_id !=
+                         netdev_get_numa_id(rxq->port->netdev)) {
+                     return true;
+@@ -5349,10 +5391,10 @@ sched_numa_list_schedule(struct sched_numa_list *numa_list,
+             /* Find any numa with available PMDs. */
+             for (int j = 0; j < n_numa; j++) {
+                 numa = sched_numa_list_next(numa_list, last_cross_numa);
++                last_cross_numa = numa;
+                 if (sched_numa_noniso_pmd_count(numa)) {
+                     break;
+                 }
+-                last_cross_numa = numa;
+                 numa = NULL;
+             }
+         }
+@@ -6616,15 +6658,15 @@ static struct dp_netdev_pmd_thread *
+ dp_netdev_get_pmd(struct dp_netdev *dp, unsigned core_id)
+ {
+     struct dp_netdev_pmd_thread *pmd;
+-    const struct cmap_node *pnode;
+ 
+-    pnode = cmap_find(&dp->poll_threads, hash_int(core_id, 0));
+-    if (!pnode) {
+-        return NULL;
++    CMAP_FOR_EACH_WITH_HASH (pmd, node, hash_int(core_id, 0),
++                             &dp->poll_threads) {
++        if (pmd->core_id == core_id) {
++            return dp_netdev_pmd_try_ref(pmd) ? pmd : NULL;
++        }
+     }
+-    pmd = CONTAINER_OF(pnode, struct dp_netdev_pmd_thread, node);
+ 
+-    return dp_netdev_pmd_try_ref(pmd) ? pmd : NULL;
++    return NULL;
+ }
+ 
+ /* Sets the 'struct dp_netdev_pmd_thread' for non-pmd threads. */
+@@ -8942,9 +8984,12 @@ dpcls_create_subtable(struct dpcls *cls, const struct netdev_flow_key *mask)
  
      /* Get the preferred subtable search function for this (u0,u1) subtable.
       * The function is guaranteed to always return a valid implementation, and
@@ -2030,7 +2098,7 @@ index bddce75b63..c56eb184d7 100644
  
      cmap_insert(&cls->subtables_map, &subtable->cmap_node, mask->hash);
      /* Add the new subtable at the end of the pvector (with no hits yet) */
-@@ -8973,6 +9013,10 @@ dpcls_find_subtable(struct dpcls *cls, const struct netdev_flow_key *mask)
+@@ -8973,6 +9018,10 @@ dpcls_find_subtable(struct dpcls *cls, const struct netdev_flow_key *mask)
  /* Checks for the best available implementation for each subtable lookup
   * function, and assigns it as the lookup function pointer for each subtable.
   * Returns the number of subtables that have changed lookup implementation.
@@ -2041,7 +2109,7 @@ index bddce75b63..c56eb184d7 100644
   */
  static uint32_t
  dpcls_subtable_lookup_reprobe(struct dpcls *cls)
-@@ -8985,10 +9029,13 @@ dpcls_subtable_lookup_reprobe(struct dpcls *cls)
+@@ -8985,10 +9034,13 @@ dpcls_subtable_lookup_reprobe(struct dpcls *cls)
          uint32_t u0_bits = subtable->mf_bits_set_unit0;
          uint32_t u1_bits = subtable->mf_bits_set_unit1;
          void *old_func = subtable->lookup_func;
@@ -4806,7 +4874,7 @@ index 114aff8ea3..0fc6d2ea60 100644
      enum xc_type type;
      union {
 diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
-index a426fcfeb6..9381affa77 100644
+index a426fcfeb6..7ba2c0462c 100644
 --- a/ofproto/ofproto-dpif-xlate.c
 +++ b/ofproto/ofproto-dpif-xlate.c
 @@ -460,7 +460,7 @@ static void xlate_commit_actions(struct xlate_ctx *ctx);
@@ -4860,6 +4928,15 @@ index a426fcfeb6..9381affa77 100644
  static struct xbundle *
  xbundle_lookup(struct xlate_cfg *xcfg, const struct ofbundle *ofbundle)
  {
+@@ -3015,7 +3032,7 @@ xlate_normal(struct xlate_ctx *ctx)
+     bool is_grat_arp = is_gratuitous_arp(flow, wc);
+     if (ctx->xin->allow_side_effects
+         && flow->packet_type == htonl(PT_ETH)
+-        && in_port->pt_mode != NETDEV_PT_LEGACY_L3
++        && in_port && in_port->pt_mode != NETDEV_PT_LEGACY_L3
+     ) {
+         update_learning_table(ctx, in_xbundle, flow->dl_src, vlan,
+                               is_grat_arp);
 @@ -3024,12 +3041,14 @@ xlate_normal(struct xlate_ctx *ctx)
          struct xc_entry *entry;
  
@@ -8073,6 +8150,63 @@ index f5a520862c..ac5b0fd0c6 100644
 +if ssl:
      # Register SSL only if the OpenSSL module is available
      Stream.register_method("ssl", SSLStream)
+diff --git a/tests/alb.at b/tests/alb.at
+index 903238fcb2..67eb14f473 100644
+--- a/tests/alb.at
++++ b/tests/alb.at
+@@ -86,6 +86,52 @@ OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
+ 
++AT_SETUP([ALB - cross-numa])
++OVS_VSWITCHD_START([add-port br0 p0 \
++                    -- set Interface p0 type=dummy-pmd options:n_rxq=4 \
++                    -- set Interface p0 options:numa_id=0 \
++                    -- set Open_vSwitch . other_config:pmd-cpu-mask=0x3 \
++                    -- set open_vswitch . other_config:pmd-rxq-assign=group \
++                    -- set open_vswitch . other_config:pmd-rxq-isolate=false \
++                    -- set open_vswitch . other_config:pmd-auto-lb="true" \
++                    -- set open_vswitch . other_config:pmd-auto-lb-load-threshold=0],
++                   [], [], [--dummy-numa 1,2,1,2])
++OVS_WAIT_UNTIL([grep "PMD auto load balance is enabled" ovs-vswitchd.log])
++AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg])
++
++# no pinned rxqs - cross-numa pmd could change
++get_log_next_line_num
++ovs-appctl time/warp 600000 10000
++OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."])
++OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"])
++
++# all pinned rxqs - cross-numa pmd will not change
++AT_CHECK([ovs-vsctl set Interface p0 other_config:pmd-rxq-affinity='0:0,1:0,2:1,3:1'])
++get_log_next_line_num
++ovs-appctl time/warp 600000 10000
++OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."])
++OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "Variance improvement 0%."])
++
++# mix of pinned (non-isolated) and non-pinned rxqs - cross-numa pmd could change
++AT_CHECK([ovs-vsctl remove Interface p0 other_config pmd-rxq-affinity])
++AT_CHECK([ovs-vsctl set Interface p0 other_config:pmd-rxq-affinity='0:0,1:0,2:1'])
++get_log_next_line_num
++ovs-appctl time/warp 600000 10000
++OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."])
++OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"])
++
++# mix of pinned (isolated) and non-pinned rxqs - cross-numa pmd could change
++AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0xf])
++AT_CHECK([ovs-vsctl set Interface p0 options:n_rxq=6])
++AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-isolate=true])
++get_log_next_line_num
++ovs-appctl time/warp 600000 10000
++OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."])
++OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
+ AT_SETUP([ALB - PMD/RxQ assignment type])
+ OVS_VSWITCHD_START([add-port br0 p0 \
+                     -- set Interface p0 type=dummy-pmd options:n_rxq=3 \
 diff --git a/tests/flowgen.py b/tests/flowgen.py
 index 7ef32d13cb..cb0e9df388 100755
 --- a/tests/flowgen.py
@@ -8106,7 +8240,7 @@ index 7ef32d13cb..cb0e9df388 100755
  
      flows.write(struct.pack('>LH',
 diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
-index 956a69e1fa..325723af19 100644
+index 956a69e1fa..266ed801e6 100644
 --- a/tests/ofproto-dpif.at
 +++ b/tests/ofproto-dpif.at
 @@ -81,11 +81,12 @@ recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=ff:
@@ -8288,7 +8422,45 @@ index 956a69e1fa..325723af19 100644
  AT_SETUP([ofproto-dpif - exit])
  OVS_VSWITCHD_START
  add_of_ports br0 1 2 3 10 11 12 13 14
-@@ -7524,7 +7572,7 @@ dnl configure bridge IPFIX and ensure that sample action generation works at the
+@@ -5525,7 +5573,36 @@ check_flows () {
+     echo "n_packets=$n"
+     test "$n" = 1
+ }
+-OVS_WAIT_UNTIL([check_flows], [ovs dump-flows br0])
++OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++# Checks for regression against a bug in which OVS crashed
++# with in_port=OFPP_NONE or in_port=OFPP_CONTROLLER and
++# recirculation is involved.
++AT_SETUP([ofproto-dpif - packet-out recirculation with OFPP_NONE and OFPP_CONTROLLER])
++OVS_VSWITCHD_START
++add_of_ports br0 1 2
++
++AT_DATA([flows.txt], [dnl
++table=0 ip actions=mod_dl_dst:83:83:83:83:83:83,ct(table=1)
++table=1 ip actions=ct(commit),normal
++])
++AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
++
++packet=ffffffffffff00102030405008004500001c00000000401100000a000002ffffffff0035111100080000
++AT_CHECK([ovs-ofctl packet-out br0 "in_port=none,packet=$packet actions=table"])
++AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller,packet=$packet actions=table"])
++
++# Dumps out the flow table, extracts the number of packets that have gone
++# through the (single) flow in table 1, and returns success if it's exactly 2.
++check_flows () {
++    n=$(ovs-ofctl dump-flows br0 table=1 | sed -n 's/.*n_packets=\([[0-9]]\{1,\}\).*/\1/p')
++    echo "n_packets=$n"
++    test "$n" = 2
++}
++OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0])
+ 
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
+@@ -7524,7 +7601,7 @@ dnl configure bridge IPFIX and ensure that sample action generation works at the
  dnl datapath level.
  AT_SETUP([ofproto-dpif - Bridge IPFIX sanity check])
  OVS_VSWITCHD_START
@@ -8297,7 +8469,7 @@ index 956a69e1fa..325723af19 100644
  
  dnl Sample every packet using bridge-based sampling.
  AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \
-@@ -7540,6 +7588,28 @@ flow-dump from the main thread:
+@@ -7540,6 +7617,28 @@ flow-dump from the main thread:
  packets:2, bytes:68, used:0.001s, actions:userspace(pid=0,ipfix(output_port=4294967295))
  ])
  
@@ -8326,7 +8498,7 @@ index 956a69e1fa..325723af19 100644
  AT_CHECK([ovs-appctl revalidator/purge])
  dnl
  dnl Add a slowpath meter. The userspace action should be metered.
-@@ -8591,6 +8661,34 @@ AT_CHECK([sed -n 's/=[[0-9]][[0-9]]\(\.[[0-9]][[0-9]]*\)\{0,1\}s/=?s/p' stdout],
+@@ -8591,6 +8690,34 @@ AT_CHECK([sed -n 's/=[[0-9]][[0-9]]\(\.[[0-9]][[0-9]]*\)\{0,1\}s/=?s/p' stdout],
  OVS_VSWITCHD_STOP
  AT_CLEANUP
  
@@ -8361,7 +8533,7 @@ index 956a69e1fa..325723af19 100644
  dnl ----------------------------------------------------------------------
  AT_BANNER([ofproto-dpif -- megaflows])
  
-@@ -9695,6 +9793,26 @@ OFPST_TABLE reply (OF1.3) (xid=0x2):
+@@ -9695,6 +9822,26 @@ OFPST_TABLE reply (OF1.3) (xid=0x2):
  OVS_VSWITCHD_STOP
  AT_CLEANUP
  
@@ -8388,7 +8560,7 @@ index 956a69e1fa..325723af19 100644
  AT_SETUP([ofproto-dpif - ICMPv6])
  OVS_VSWITCHD_START
  add_of_ports br0 1
-@@ -11404,6 +11522,23 @@ Megaflow: recirc_id=0x3,eth,ip,in_port=1,nw_frag=no
+@@ -11404,6 +11551,23 @@ Megaflow: recirc_id=0x3,eth,ip,in_port=1,nw_frag=no
  Datapath actions: 4
  ])
  
@@ -8744,6 +8916,64 @@ index ac243d6a79..876cb836cd 100644
  OVS_APP_EXIT_AND_WAIT([ovsdb-server])
  AT_CLEANUP
  
+diff --git a/tests/pmd.at b/tests/pmd.at
+index 225d4ee3a4..a7cbf9a81b 100644
+--- a/tests/pmd.at
++++ b/tests/pmd.at
+@@ -199,7 +199,7 @@ pmd thread numa_id <cleared> core_id <cleared>:
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
+ 
+-AT_SETUP([PMD - pmd-cpu-mask - NUMA])
++AT_SETUP([PMD - pmd-cpu-mask - dual NUMA])
+ OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd options:n_rxq=8 options:numa_id=1 -- set Open_vSwitch . other_config:pmd-cpu-mask=1],
+                    [], [], [--dummy-numa 1,1,0,0])
+ 
+@@ -359,6 +359,44 @@ pmd thread numa_id 1 core_id 0:
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
+ 
++AT_SETUP([PMD - pmd-cpu-mask - multi NUMA])
++OVS_VSWITCHD_START([add-port br0 p0 \
++                    -- set Interface p0 type=dummy-pmd options:n_rxq=4 \
++                    -- set Interface p0 options:numa_id=0 \
++                    -- set Open_vSwitch . other_config:pmd-cpu-mask=0xf \
++                    -- set open_vswitch . other_config:pmd-rxq-assign=cycles],
++                   [], [], [--dummy-numa 1,2,1,2])
++
++TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1))
++AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group])
++
++OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using group algorithm"])
++OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."])
++
++# check all pmds from both non-local numas are assigned an rxq
++AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4])
++
++TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1))
++AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=cycles])
++
++OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using cycles algorithm"])
++OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."])
++
++# check all pmds from both non-local numas are assigned an rxq
++AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4])
++
++TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1))
++AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=roundrobin])
++
++OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using roundrobin algorithm"])
++OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."])
++
++# check all pmds from both non-local numas are assigned an rxq
++AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
+ AT_SETUP([PMD - stats])
+ OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 ofport_request=7 type=dummy-pmd options:n_rxq=4],
+                    [], [], [DUMMY_NUMA])
 diff --git a/tests/reconnect.at b/tests/reconnect.at
 index 0f74709f5a..5bca84351c 100644
 --- a/tests/reconnect.at
diff --git a/SPECS/openvswitch2.16.spec b/SPECS/openvswitch2.16.spec
index 7c886c3..01623a7 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: 65%{?dist}
+Release: 66%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -699,6 +699,17 @@ exit 0
 %endif
 
 %changelog
+* Mon Apr 04 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.16.0-66
+- Merging upstream branch-2.16 [RH git: 1418edaf18]
+    Commit list:
+    26189fd264 dpif-netdev: Fix dp_netdev_get_pmd() function getting correct core_id.
+    a5af081bc6 alb.at: Add tests for cross-numa polling.
+    78c8f8a7f6 dpif-netdev: Fix PMD auto load balance with pmd-rxq-isolate.
+    6731e581c4 pmd.at: Add tests for multi non-local numa pmds.
+    60652bb3eb dpif-netdev: Fix non-local numa selection for more than two numas.
+    c113039503 ofproto-dpif-xlate: Fix NULL pointer dereference in xlate_normal().
+
+
 * Wed Mar 30 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.16.0-65
 - Merging upstream branch-2.16 [RH git: b4c45acc47]
     Commit list: