On Tue, Aug 18, 2026 at 2:27 AM Dumitru Ceara <[email protected]> wrote:
>
> On 7/22/26 7:49 AM, Han Zhou wrote:
> > OVS 3.7 (OVS commit 3900653) increased FLOW_N_REGS from 16 to 32, making
> > registers reg16..reg31 (xxreg4..xxreg7) usable. However, whether the
> > running ovs-vswitchd actually supports them depends on its version, so
> > OVN must detect this at runtime before relying on those registers.
> >
> > Add an OVS OpenFlow feature probe (OVS_REG32_SUPPORT) that pushes a
> > discarded bundle containing a flow_mod matching reg16. An ovs-vswitchd
> > that does not support 32 registers rejects it with an error; a newer one
> > accepts it, so the barrier is reached without error.
> >
> > ovn-controller advertises the result to the SB Chassis via
> > other_config:ovn-32-registers, and ovn-northd aggregates it across all
> > local chassis into chassis_features.reg32. This lets later features use
> > registers beyond reg15 only when every chassis supports them, and fall
> > back otherwise.
> >
> > Assisted-by: Claude Opus 4.8, Cursor
> > Signed-off-by: Han Zhou <[email protected]>
> > ---
>
> Hi Han,
>
> This is not really needed for the "Support per-route VNI for learned
> EVPN routes." patch-set as I see you reworked it without new registers.
> But I think it would be a nice to have feature. I'm sure we will need
> the detection mechanism in the near future.
>
> Do you mind posting this one separately as a regular patch?
Thanks Dumitru. Could you review this patch as a separate one? If it looks
good, I can merge this one and post the 2nd patch again.
Best,
Han
>
> Regards,
> Dumitru
>
> > controller/chassis.c | 12 +++++++
> > include/ovn/features.h | 6 ++++
> > lib/features.c | 75 +++++++++++++++++++++++++++++++++++++++
> > northd/en-global-config.c | 13 +++++++
> > northd/en-global-config.h | 1 +
> > 5 files changed, 107 insertions(+)
> >
> > diff --git a/controller/chassis.c b/controller/chassis.c
> > index 0e3bd37d8752..2160c84d5c91 100644
> > --- a/controller/chassis.c
> > +++ b/controller/chassis.c
> > @@ -74,6 +74,8 @@ struct ovs_chassis_cfg {
> > bool sample_with_regs;
> > /* Does OVS support flushing CT zones using label/mark? */
> > bool ct_label_flush;
> > + /* Does OVS support 32 registers (reg16..reg31)? */
> > + bool reg32;
> > };
> >
> > enum chassis_update_status {
> > @@ -385,6 +387,7 @@ chassis_parse_ovs_config(const struct
ovsrec_open_vswitch_table *ovs_table,
> > ovs_feature_is_supported(OVS_SAMPLE_REG_SUPPORT);
> > ovs_cfg->ct_label_flush =
> > ovs_feature_is_supported(OVS_CT_LABEL_FLUSH_SUPPORT);
> > + ovs_cfg->reg32 = ovs_feature_is_supported(OVS_REG32_SUPPORT);
> >
> > return true;
> > }
> > @@ -424,6 +427,8 @@ chassis_build_other_config(const struct
ovs_chassis_cfg *ovs_cfg,
> > smap_replace(config, OVN_FEATURE_CT_LABEL_FLUSH,
> > ovs_cfg->ct_label_flush ? "true" :"false");
> > smap_replace(config, OVN_FEATURE_CT_STATE_SAVE, "true");
> > + smap_replace(config, OVN_FEATURE_REG32,
> > + ovs_cfg->reg32 ? "true" : "false");
> > }
> >
> > /*
> > @@ -597,6 +602,12 @@ chassis_other_config_changed(const struct
ovs_chassis_cfg *ovs_cfg,
> > return true;
> > }
> >
> > + bool chassis_reg32 =
> > + smap_get_bool(&chassis_rec->other_config, OVN_FEATURE_REG32,
false);
> > + if (chassis_reg32 != ovs_cfg->reg32) {
> > + return true;
> > + }
> > +
> > return false;
> > }
> >
> > @@ -779,6 +790,7 @@ update_supported_sset(struct sset *supported)
> > sset_add(supported, OVN_FEATURE_CT_NEXT_ZONE);
> > sset_add(supported, OVN_FEATURE_CT_LABEL_FLUSH);
> > sset_add(supported, OVN_FEATURE_CT_STATE_SAVE);
> > + sset_add(supported, OVN_FEATURE_REG32);
> > }
> >
> > static void
> > diff --git a/include/ovn/features.h b/include/ovn/features.h
> > index 7207da67fd20..bb7c85ea5cff 100644
> > --- a/include/ovn/features.h
> > +++ b/include/ovn/features.h
> > @@ -27,6 +27,7 @@
> > #define OVN_FEATURE_CT_NEXT_ZONE "ct-next-zone"
> > #define OVN_FEATURE_CT_LABEL_FLUSH "ct-label-flush"
> > #define OVN_FEATURE_CT_STATE_SAVE "ct-state-save"
> > +#define OVN_FEATURE_REG32 "ovn-32-registers"
> >
> > /* DEPRECATED: The following features can be removed
> > * after the next LTS version release. */
> > @@ -45,6 +46,7 @@ enum ovs_feature_support_bits {
> > OVS_OF_GROUP_SUPPORT_BIT,
> > OVS_SAMPLE_REG_SUPPORT_BIT,
> > OVS_CT_LABEL_FLUSH_BIT,
> > + OVS_REG32_SUPPORT_BIT,
> > };
> >
> > enum ovs_feature_value {
> > @@ -55,6 +57,10 @@ enum ovs_feature_value {
> > OVS_OF_GROUP_SUPPORT = (1 << OVS_OF_GROUP_SUPPORT_BIT),
> > OVS_SAMPLE_REG_SUPPORT = (1 << OVS_SAMPLE_REG_SUPPORT_BIT),
> > OVS_CT_LABEL_FLUSH_SUPPORT = (1 << OVS_CT_LABEL_FLUSH_BIT),
> > + /* OVS (and its OpenFlow interface) supports 32 32-bit registers,
> > + * i.e. registers reg16..reg31 (xxreg4..xxreg7) are usable. Older
OVS
> > + * versions only support 16 registers. */
> > + OVS_REG32_SUPPORT = (1 << OVS_REG32_SUPPORT_BIT),
> > };
> >
> > void ovs_feature_support_destroy(void);
> > diff --git a/lib/features.c b/lib/features.c
> > index 7bee1c3806e6..b787bf7e8529 100644
> > --- a/lib/features.c
> > +++ b/lib/features.c
> > @@ -269,6 +269,73 @@ sample_with_reg_handle_barrier(struct
ovs_openflow_feature *feature OVS_UNUSED)
> > return true;
> > }
> >
> > +static void
> > +reg32_send_request(struct ovs_openflow_feature *feature)
> > +{
> > + struct ofputil_bundle_ctrl_msg ctrl = {
> > + .bundle_id = 0,
> > + .flags = OFPBF_ORDERED | OFPBF_ATOMIC,
> > + .type = OFPBCT_OPEN_REQUEST,
> > + };
> > + rconn_send(swconn,
> > + ofputil_encode_bundle_ctrl_request(OFP15_VERSION,
&ctrl), NULL);
> > +
> > + struct ofputil_flow_mod fm = {
> > + .priority = 0,
> > + .table_id = 0,
> > + .ofpacts = NULL,
> > + .ofpacts_len = 0,
> > + .command = OFPFC_ADD,
> > + .new_cookie = htonll(0),
> > + .buffer_id = UINT32_MAX,
> > + .out_port = OFPP_ANY,
> > + .out_group = OFPG_ANY,
> > + };
> > +
> > + /* Match on reg16, which only exists when the running OVS supports
32
> > + * registers. Older OVS versions reject the flow_mod with an
error. */
> > + struct match match;
> > + match_init_catchall(&match);
> > + match_set_reg(&match, MFF_REG16 - MFF_REG0, 1);
> > + minimatch_init(&fm.match, &match);
> > +
> > + struct ofpbuf *fm_msg = ofputil_encode_flow_mod(&fm,
OFPUTIL_P_OF15_OXM);
> > +
> > + struct ofputil_bundle_add_msg bam = {
> > + .bundle_id = ctrl.bundle_id,
> > + .flags = ctrl.flags,
> > + .msg = fm_msg->data,
> > + };
> > + struct ofpbuf *msg = ofputil_encode_bundle_add(OFP15_VERSION,
&bam);
> > +
> > + feature->xid = ((struct ofp_header *) msg->data)->xid;
> > + rconn_send(swconn, msg, NULL);
> > +
> > + ctrl.type = OFPBCT_DISCARD_REQUEST;
> > + rconn_send(swconn,
> > + ofputil_encode_bundle_ctrl_request(OFP15_VERSION,
&ctrl), NULL);
> > +
> > + minimatch_destroy(&fm.match);
> > + ofpbuf_delete(fm_msg);
> > +}
> > +
> > +static bool
> > +reg32_handle_response(struct ovs_openflow_feature *feature,
> > + enum ofptype type, const struct ofp_header *oh)
> > +{
> > + if (type != OFPTYPE_ERROR) {
> > + log_unexpected_reply(feature, oh);
> > + }
> > +
> > + return false;
> > +}
> > +
> > +static bool
> > +reg32_handle_barrier(struct ovs_openflow_feature *feature OVS_UNUSED)
> > +{
> > + return true;
> > +}
> > +
> > static void
> > ct_label_flush_send_request(struct ovs_openflow_feature *feature)
> > {
> > @@ -345,6 +412,13 @@ static struct ovs_openflow_feature
all_openflow_features[] = {
> > .handle_response = ct_label_flush_handle_response,
> > .handle_barrier = ct_label_flush_handle_barrier,
> > },
> > + {
> > + .value = OVS_REG32_SUPPORT,
> > + .name = "reg32",
> > + .send_request = reg32_send_request,
> > + .handle_response = reg32_handle_response,
> > + .handle_barrier = reg32_handle_barrier,
> > + },
> > };
> >
> > static bool
> > @@ -418,6 +492,7 @@ ovs_feature_is_valid(enum ovs_feature_value feature)
> > case OVS_OF_GROUP_SUPPORT:
> > case OVS_SAMPLE_REG_SUPPORT:
> > case OVS_CT_LABEL_FLUSH_SUPPORT:
> > + case OVS_REG32_SUPPORT:
> > return true;
> > default:
> > return false;
> > diff --git a/northd/en-global-config.c b/northd/en-global-config.c
> > index 4e6b07ebe50e..0667e11ffe8a 100644
> > --- a/northd/en-global-config.c
> > +++ b/northd/en-global-config.c
> > @@ -520,6 +520,7 @@ northd_enable_all_features(struct
ed_type_global_config *data)
> > .ct_next_zone = true,
> > .ct_label_flush = true,
> > .ct_state_save = true,
> > + .reg32 = true,
> > };
> > }
> >
> > @@ -590,6 +591,14 @@ build_chassis_features(const struct
sbrec_chassis_table *sbrec_chassis_table,
> > chassis_features->ct_state_save) {
> > chassis_features->ct_state_save = false;
> > }
> > +
> > + bool reg32 =
> > + smap_get_bool(&chassis->other_config,
> > + OVN_FEATURE_REG32,
> > + false);
> > + if (!reg32 && chassis_features->reg32) {
> > + chassis_features->reg32 = false;
> > + }
> > }
> > }
> >
> > @@ -747,6 +756,10 @@ chassis_features_changed(const struct
chassis_features *present,
> > return true;
> > }
> >
> > + if (present->reg32 != updated->reg32) {
> > + return true;
> > + }
> > +
> > return false;
> > }
> >
> > diff --git a/northd/en-global-config.h b/northd/en-global-config.h
> > index 5a7b7ecda766..9b75ecff3d21 100644
> > --- a/northd/en-global-config.h
> > +++ b/northd/en-global-config.h
> > @@ -20,6 +20,7 @@ struct chassis_features {
> > bool ct_next_zone;
> > bool ct_label_flush;
> > bool ct_state_save;
> > + bool reg32;
> > };
> >
> > struct global_config_tracked_data {
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev