Re: [ovs-dev] [PATCH ovn] northd: Improve the LB affinity code

2022-11-24 Thread Dumitru Ceara
On 11/24/22 13:08, Ales Musil wrote:
> On Thu, Nov 24, 2022 at 12:44 PM Dumitru Ceara  wrote:
> 
>> On 11/24/22 07:37, Ales Musil wrote:
>>> Improve the affinity code to reuse ds buffers as much as possible
>>> without constantly repeating some parts. Add ct.new for the LB flows
>>> so it is clear that the commit happens only when we have a new
>>> connection.
>>>
>>> Signed-off-by: Ales Musil 
>>> ---
>>
>> Hi Ales,
>>
>>>  northd/northd.c | 162 ++--
>>>  northd/ovn-northd.8.xml |  15 ++--
>>>  tests/ovn-northd.at |  10 +--
>>>  tests/system-ovn.at |   8 +-
>>>  4 files changed, 91 insertions(+), 104 deletions(-)
>>>
>>> diff --git a/northd/northd.c b/northd/northd.c
>>> index 040f46e1a..188042bca 100644
>>> --- a/northd/northd.c
>>> +++ b/northd/northd.c
>>> @@ -243,7 +243,6 @@ enum ovn_stage {
>>>  #define REGBIT_DST_NAT_IP_LOCAL "reg9[4]"
>>>  #define REGBIT_KNOWN_ECMP_NH"reg9[5]"
>>>  #define REGBIT_KNOWN_LB_SESSION "reg9[6]"
>>> -#define REG
>>
>> Oops, I thought I had fixed this up before applying the original patch.
>> I guess not.  Thanks for spotting it!
>>
>>>
>>>  /* Register to store the eth address associated to a router port for
>> packets
>>>   * received in S_ROUTER_IN_ADMISSION.
>>> @@ -6963,6 +6962,7 @@ build_lb_affinity_flows(struct hmap *lflows,
>> struct ovn_northd_lb *lb,
>>>  return;
>>>  }
>>>
>>> +static char *aff_check = REGBIT_KNOWN_LB_SESSION" = chk_lb_aff();
>> next;";
>>>  enum ovn_stage stage0 = router_pipeline ?
>>>  S_ROUTER_IN_LB_AFF_CHECK : S_SWITCH_IN_LB_AFF_CHECK;
>>>  struct ovn_lflow *lflow_ref_aff_check = NULL;
>>> @@ -6970,80 +6970,102 @@ build_lb_affinity_flows(struct hmap *lflows,
>> struct ovn_northd_lb *lb,
>>>   * tuple and we are in affinity timeslot. */
>>>  uint32_t hash_aff_check = ovn_logical_flow_hash(
>>>  ovn_stage_get_table(stage0),
>> ovn_stage_get_pipeline(stage0), 100,
>>> -check_lb_match, REGBIT_KNOWN_LB_SESSION" = chk_lb_aff();
>> next;");
>>> +check_lb_match, aff_check);
>>>
>>>  for (size_t i = 0; i < n_dplist; i++) {
>>>  if (!ovn_dp_group_add_with_reference(lflow_ref_aff_check,
>> dplist[i])) {
>>>  lflow_ref_aff_check = ovn_lflow_add_at_with_hash(
>>>  lflows, dplist[i], stage0, 100, check_lb_match,
>>> -REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;",
>>> -NULL, NULL, >nlb->header_,
>>> +aff_check, NULL, NULL, >nlb->header_,
>>>  OVS_SOURCE_LOCATOR, hash_aff_check);
>>>  }
>>>  }
>>>
>>> +const char *reg_vip;
>>> +const char *reg_backend;
>>> +
>>> +struct ds aff_action = DS_EMPTY_INITIALIZER;
>>>  struct ds aff_action_learn = DS_EMPTY_INITIALIZER;
>>> -struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
>>> -struct ds aff_action_lb_common = DS_EMPTY_INITIALIZER;
>>> -struct ds aff_action_lb = DS_EMPTY_INITIALIZER;
>>>  struct ds aff_match = DS_EMPTY_INITIALIZER;
>>> +struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
>>>
>>>  bool ipv6 = !IN6_IS_ADDR_V4MAPPED(_vip->vip);
>>> -const char *reg_vip;
>>> +const char *ip_match = ipv6 ? "ip6" : "ip4";
>>> +
>>> +stage0 =
>>> +router_pipeline ? S_ROUTER_IN_LB_AFF_LEARN :
>> S_SWITCH_IN_LB_AFF_LEARN;
>>> +enum ovn_stage stage1 =
>>> +router_pipeline ? S_ROUTER_IN_DNAT : S_SWITCH_IN_LB;
>>> +
>>>  if (router_pipeline) {
>>>  reg_vip = ipv6 ? REG_NEXT_HOP_IPV6 : REG_NEXT_HOP_IPV4;
>>> +reg_backend =
>>> +ipv6 ? REG_LB_L3_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
>>>  } else {
>>>  reg_vip = ipv6 ? REG_ORIG_DIP_IPV6 : REG_ORIG_DIP_IPV4;
>>> +reg_backend =
>>> +ipv6 ? REG_LB_L2_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
>>>  }
>>>
>>> -ds_put_format(_action_lb_common,
>>> -  REGBIT_CONNTRACK_COMMIT" = 0; %s = %s; ",
>>> +/* Prepare common part of affinity LB and affinity learn action. */
>>> +ds_put_format(_action, REGBIT_CONNTRACK_COMMIT" = 0; %s = %s; ",
>>>reg_vip, lb_vip->vip_str);
>>> +ds_put_cstr(_action_learn, "commit_lb_aff(vip = \"");
>>> +
>>>  if (lb_vip->vip_port) {
>>> -ds_put_format(_action_lb_common, REG_ORIG_TP_DPORT" = %d; ",
>>> +ds_put_format(_action, REG_ORIG_TP_DPORT" = %d; ",
>>>lb_vip->vip_port);
>>> +ds_put_format(_action_learn, ipv6 ? "[%s]:%d" : "%s:%d",
>>> +  lb_vip->vip_str, lb_vip->vip_port);
>>> +} else {
>>> +ds_put_cstr(_action_learn, lb_vip->vip_str);
>>>  }
>>>
>>>  if (lb_action) {
>>> -ds_put_format(_action_lb_common, "%s;", lb_action);
>>> +ds_put_cstr(_action, lb_action);
>>>  }
>>> +ds_put_cstr(_action, "ct_lb_mark(backends=");
>>> +ds_put_cstr(_action_learn, "\", backend = \"");
>>> +
>>> +

Re: [ovs-dev] [PATCH ovn] northd: Improve the LB affinity code

2022-11-24 Thread Ales Musil
On Thu, Nov 24, 2022 at 12:44 PM Dumitru Ceara  wrote:

> On 11/24/22 07:37, Ales Musil wrote:
> > Improve the affinity code to reuse ds buffers as much as possible
> > without constantly repeating some parts. Add ct.new for the LB flows
> > so it is clear that the commit happens only when we have a new
> > connection.
> >
> > Signed-off-by: Ales Musil 
> > ---
>
> Hi Ales,
>
> >  northd/northd.c | 162 ++--
> >  northd/ovn-northd.8.xml |  15 ++--
> >  tests/ovn-northd.at |  10 +--
> >  tests/system-ovn.at |   8 +-
> >  4 files changed, 91 insertions(+), 104 deletions(-)
> >
> > diff --git a/northd/northd.c b/northd/northd.c
> > index 040f46e1a..188042bca 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -243,7 +243,6 @@ enum ovn_stage {
> >  #define REGBIT_DST_NAT_IP_LOCAL "reg9[4]"
> >  #define REGBIT_KNOWN_ECMP_NH"reg9[5]"
> >  #define REGBIT_KNOWN_LB_SESSION "reg9[6]"
> > -#define REG
>
> Oops, I thought I had fixed this up before applying the original patch.
> I guess not.  Thanks for spotting it!
>
> >
> >  /* Register to store the eth address associated to a router port for
> packets
> >   * received in S_ROUTER_IN_ADMISSION.
> > @@ -6963,6 +6962,7 @@ build_lb_affinity_flows(struct hmap *lflows,
> struct ovn_northd_lb *lb,
> >  return;
> >  }
> >
> > +static char *aff_check = REGBIT_KNOWN_LB_SESSION" = chk_lb_aff();
> next;";
> >  enum ovn_stage stage0 = router_pipeline ?
> >  S_ROUTER_IN_LB_AFF_CHECK : S_SWITCH_IN_LB_AFF_CHECK;
> >  struct ovn_lflow *lflow_ref_aff_check = NULL;
> > @@ -6970,80 +6970,102 @@ build_lb_affinity_flows(struct hmap *lflows,
> struct ovn_northd_lb *lb,
> >   * tuple and we are in affinity timeslot. */
> >  uint32_t hash_aff_check = ovn_logical_flow_hash(
> >  ovn_stage_get_table(stage0),
> ovn_stage_get_pipeline(stage0), 100,
> > -check_lb_match, REGBIT_KNOWN_LB_SESSION" = chk_lb_aff();
> next;");
> > +check_lb_match, aff_check);
> >
> >  for (size_t i = 0; i < n_dplist; i++) {
> >  if (!ovn_dp_group_add_with_reference(lflow_ref_aff_check,
> dplist[i])) {
> >  lflow_ref_aff_check = ovn_lflow_add_at_with_hash(
> >  lflows, dplist[i], stage0, 100, check_lb_match,
> > -REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;",
> > -NULL, NULL, >nlb->header_,
> > +aff_check, NULL, NULL, >nlb->header_,
> >  OVS_SOURCE_LOCATOR, hash_aff_check);
> >  }
> >  }
> >
> > +const char *reg_vip;
> > +const char *reg_backend;
> > +
> > +struct ds aff_action = DS_EMPTY_INITIALIZER;
> >  struct ds aff_action_learn = DS_EMPTY_INITIALIZER;
> > -struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
> > -struct ds aff_action_lb_common = DS_EMPTY_INITIALIZER;
> > -struct ds aff_action_lb = DS_EMPTY_INITIALIZER;
> >  struct ds aff_match = DS_EMPTY_INITIALIZER;
> > +struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
> >
> >  bool ipv6 = !IN6_IS_ADDR_V4MAPPED(_vip->vip);
> > -const char *reg_vip;
> > +const char *ip_match = ipv6 ? "ip6" : "ip4";
> > +
> > +stage0 =
> > +router_pipeline ? S_ROUTER_IN_LB_AFF_LEARN :
> S_SWITCH_IN_LB_AFF_LEARN;
> > +enum ovn_stage stage1 =
> > +router_pipeline ? S_ROUTER_IN_DNAT : S_SWITCH_IN_LB;
> > +
> >  if (router_pipeline) {
> >  reg_vip = ipv6 ? REG_NEXT_HOP_IPV6 : REG_NEXT_HOP_IPV4;
> > +reg_backend =
> > +ipv6 ? REG_LB_L3_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
> >  } else {
> >  reg_vip = ipv6 ? REG_ORIG_DIP_IPV6 : REG_ORIG_DIP_IPV4;
> > +reg_backend =
> > +ipv6 ? REG_LB_L2_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
> >  }
> >
> > -ds_put_format(_action_lb_common,
> > -  REGBIT_CONNTRACK_COMMIT" = 0; %s = %s; ",
> > +/* Prepare common part of affinity LB and affinity learn action. */
> > +ds_put_format(_action, REGBIT_CONNTRACK_COMMIT" = 0; %s = %s; ",
> >reg_vip, lb_vip->vip_str);
> > +ds_put_cstr(_action_learn, "commit_lb_aff(vip = \"");
> > +
> >  if (lb_vip->vip_port) {
> > -ds_put_format(_action_lb_common, REG_ORIG_TP_DPORT" = %d; ",
> > +ds_put_format(_action, REG_ORIG_TP_DPORT" = %d; ",
> >lb_vip->vip_port);
> > +ds_put_format(_action_learn, ipv6 ? "[%s]:%d" : "%s:%d",
> > +  lb_vip->vip_str, lb_vip->vip_port);
> > +} else {
> > +ds_put_cstr(_action_learn, lb_vip->vip_str);
> >  }
> >
> >  if (lb_action) {
> > -ds_put_format(_action_lb_common, "%s;", lb_action);
> > +ds_put_cstr(_action, lb_action);
> >  }
> > +ds_put_cstr(_action, "ct_lb_mark(backends=");
> > +ds_put_cstr(_action_learn, "\", backend = \"");
> > +
> > +/* Prepare common part of affinity learn match. */
> > +   

Re: [ovs-dev] [PATCH ovn] northd: Improve the LB affinity code

2022-11-24 Thread Dumitru Ceara
On 11/24/22 07:37, Ales Musil wrote:
> Improve the affinity code to reuse ds buffers as much as possible
> without constantly repeating some parts. Add ct.new for the LB flows
> so it is clear that the commit happens only when we have a new
> connection.
> 
> Signed-off-by: Ales Musil 
> ---

Hi Ales,

>  northd/northd.c | 162 ++--
>  northd/ovn-northd.8.xml |  15 ++--
>  tests/ovn-northd.at |  10 +--
>  tests/system-ovn.at |   8 +-
>  4 files changed, 91 insertions(+), 104 deletions(-)
> 
> diff --git a/northd/northd.c b/northd/northd.c
> index 040f46e1a..188042bca 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -243,7 +243,6 @@ enum ovn_stage {
>  #define REGBIT_DST_NAT_IP_LOCAL "reg9[4]"
>  #define REGBIT_KNOWN_ECMP_NH"reg9[5]"
>  #define REGBIT_KNOWN_LB_SESSION "reg9[6]"
> -#define REG

Oops, I thought I had fixed this up before applying the original patch.
I guess not.  Thanks for spotting it!

>  
>  /* Register to store the eth address associated to a router port for packets
>   * received in S_ROUTER_IN_ADMISSION.
> @@ -6963,6 +6962,7 @@ build_lb_affinity_flows(struct hmap *lflows, struct 
> ovn_northd_lb *lb,
>  return;
>  }
>  
> +static char *aff_check = REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;";
>  enum ovn_stage stage0 = router_pipeline ?
>  S_ROUTER_IN_LB_AFF_CHECK : S_SWITCH_IN_LB_AFF_CHECK;
>  struct ovn_lflow *lflow_ref_aff_check = NULL;
> @@ -6970,80 +6970,102 @@ build_lb_affinity_flows(struct hmap *lflows, struct 
> ovn_northd_lb *lb,
>   * tuple and we are in affinity timeslot. */
>  uint32_t hash_aff_check = ovn_logical_flow_hash(
>  ovn_stage_get_table(stage0), ovn_stage_get_pipeline(stage0), 100,
> -check_lb_match, REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;");
> +check_lb_match, aff_check);
>  
>  for (size_t i = 0; i < n_dplist; i++) {
>  if (!ovn_dp_group_add_with_reference(lflow_ref_aff_check, 
> dplist[i])) {
>  lflow_ref_aff_check = ovn_lflow_add_at_with_hash(
>  lflows, dplist[i], stage0, 100, check_lb_match,
> -REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;",
> -NULL, NULL, >nlb->header_,
> +aff_check, NULL, NULL, >nlb->header_,
>  OVS_SOURCE_LOCATOR, hash_aff_check);
>  }
>  }
>  
> +const char *reg_vip;
> +const char *reg_backend;
> +
> +struct ds aff_action = DS_EMPTY_INITIALIZER;
>  struct ds aff_action_learn = DS_EMPTY_INITIALIZER;
> -struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
> -struct ds aff_action_lb_common = DS_EMPTY_INITIALIZER;
> -struct ds aff_action_lb = DS_EMPTY_INITIALIZER;
>  struct ds aff_match = DS_EMPTY_INITIALIZER;
> +struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
>  
>  bool ipv6 = !IN6_IS_ADDR_V4MAPPED(_vip->vip);
> -const char *reg_vip;
> +const char *ip_match = ipv6 ? "ip6" : "ip4";
> +
> +stage0 =
> +router_pipeline ? S_ROUTER_IN_LB_AFF_LEARN : 
> S_SWITCH_IN_LB_AFF_LEARN;
> +enum ovn_stage stage1 =
> +router_pipeline ? S_ROUTER_IN_DNAT : S_SWITCH_IN_LB;
> +
>  if (router_pipeline) {
>  reg_vip = ipv6 ? REG_NEXT_HOP_IPV6 : REG_NEXT_HOP_IPV4;
> +reg_backend =
> +ipv6 ? REG_LB_L3_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
>  } else {
>  reg_vip = ipv6 ? REG_ORIG_DIP_IPV6 : REG_ORIG_DIP_IPV4;
> +reg_backend =
> +ipv6 ? REG_LB_L2_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
>  }
>  
> -ds_put_format(_action_lb_common,
> -  REGBIT_CONNTRACK_COMMIT" = 0; %s = %s; ",
> +/* Prepare common part of affinity LB and affinity learn action. */
> +ds_put_format(_action, REGBIT_CONNTRACK_COMMIT" = 0; %s = %s; ",
>reg_vip, lb_vip->vip_str);
> +ds_put_cstr(_action_learn, "commit_lb_aff(vip = \"");
> +
>  if (lb_vip->vip_port) {
> -ds_put_format(_action_lb_common, REG_ORIG_TP_DPORT" = %d; ",
> +ds_put_format(_action, REG_ORIG_TP_DPORT" = %d; ",
>lb_vip->vip_port);
> +ds_put_format(_action_learn, ipv6 ? "[%s]:%d" : "%s:%d",
> +  lb_vip->vip_str, lb_vip->vip_port);
> +} else {
> +ds_put_cstr(_action_learn, lb_vip->vip_str);
>  }
>  
>  if (lb_action) {
> -ds_put_format(_action_lb_common, "%s;", lb_action);
> +ds_put_cstr(_action, lb_action);
>  }
> +ds_put_cstr(_action, "ct_lb_mark(backends=");
> +ds_put_cstr(_action_learn, "\", backend = \"");
> +
> +/* Prepare common part of affinity learn match. */
> +ds_put_format(_match_learn, REGBIT_KNOWN_LB_SESSION" == 0 && "
> +  "ct.new && %s && %s == %s && %s.dst == ", ip_match,
> +  reg_vip, lb_vip->vip_str, ip_match);
> +
> +/* Prepare common part of affinity match. */
> +

[ovs-dev] [PATCH ovn] northd: Improve the LB affinity code

2022-11-23 Thread Ales Musil
Improve the affinity code to reuse ds buffers as much as possible
without constantly repeating some parts. Add ct.new for the LB flows
so it is clear that the commit happens only when we have a new
connection.

Signed-off-by: Ales Musil 
---
 northd/northd.c | 162 ++--
 northd/ovn-northd.8.xml |  15 ++--
 tests/ovn-northd.at |  10 +--
 tests/system-ovn.at |   8 +-
 4 files changed, 91 insertions(+), 104 deletions(-)

diff --git a/northd/northd.c b/northd/northd.c
index 040f46e1a..188042bca 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -243,7 +243,6 @@ enum ovn_stage {
 #define REGBIT_DST_NAT_IP_LOCAL "reg9[4]"
 #define REGBIT_KNOWN_ECMP_NH"reg9[5]"
 #define REGBIT_KNOWN_LB_SESSION "reg9[6]"
-#define REG
 
 /* Register to store the eth address associated to a router port for packets
  * received in S_ROUTER_IN_ADMISSION.
@@ -6963,6 +6962,7 @@ build_lb_affinity_flows(struct hmap *lflows, struct 
ovn_northd_lb *lb,
 return;
 }
 
+static char *aff_check = REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;";
 enum ovn_stage stage0 = router_pipeline ?
 S_ROUTER_IN_LB_AFF_CHECK : S_SWITCH_IN_LB_AFF_CHECK;
 struct ovn_lflow *lflow_ref_aff_check = NULL;
@@ -6970,80 +6970,102 @@ build_lb_affinity_flows(struct hmap *lflows, struct 
ovn_northd_lb *lb,
  * tuple and we are in affinity timeslot. */
 uint32_t hash_aff_check = ovn_logical_flow_hash(
 ovn_stage_get_table(stage0), ovn_stage_get_pipeline(stage0), 100,
-check_lb_match, REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;");
+check_lb_match, aff_check);
 
 for (size_t i = 0; i < n_dplist; i++) {
 if (!ovn_dp_group_add_with_reference(lflow_ref_aff_check, dplist[i])) {
 lflow_ref_aff_check = ovn_lflow_add_at_with_hash(
 lflows, dplist[i], stage0, 100, check_lb_match,
-REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;",
-NULL, NULL, >nlb->header_,
+aff_check, NULL, NULL, >nlb->header_,
 OVS_SOURCE_LOCATOR, hash_aff_check);
 }
 }
 
+const char *reg_vip;
+const char *reg_backend;
+
+struct ds aff_action = DS_EMPTY_INITIALIZER;
 struct ds aff_action_learn = DS_EMPTY_INITIALIZER;
-struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
-struct ds aff_action_lb_common = DS_EMPTY_INITIALIZER;
-struct ds aff_action_lb = DS_EMPTY_INITIALIZER;
 struct ds aff_match = DS_EMPTY_INITIALIZER;
+struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
 
 bool ipv6 = !IN6_IS_ADDR_V4MAPPED(_vip->vip);
-const char *reg_vip;
+const char *ip_match = ipv6 ? "ip6" : "ip4";
+
+stage0 =
+router_pipeline ? S_ROUTER_IN_LB_AFF_LEARN : S_SWITCH_IN_LB_AFF_LEARN;
+enum ovn_stage stage1 =
+router_pipeline ? S_ROUTER_IN_DNAT : S_SWITCH_IN_LB;
+
 if (router_pipeline) {
 reg_vip = ipv6 ? REG_NEXT_HOP_IPV6 : REG_NEXT_HOP_IPV4;
+reg_backend =
+ipv6 ? REG_LB_L3_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
 } else {
 reg_vip = ipv6 ? REG_ORIG_DIP_IPV6 : REG_ORIG_DIP_IPV4;
+reg_backend =
+ipv6 ? REG_LB_L2_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
 }
 
-ds_put_format(_action_lb_common,
-  REGBIT_CONNTRACK_COMMIT" = 0; %s = %s; ",
+/* Prepare common part of affinity LB and affinity learn action. */
+ds_put_format(_action, REGBIT_CONNTRACK_COMMIT" = 0; %s = %s; ",
   reg_vip, lb_vip->vip_str);
+ds_put_cstr(_action_learn, "commit_lb_aff(vip = \"");
+
 if (lb_vip->vip_port) {
-ds_put_format(_action_lb_common, REG_ORIG_TP_DPORT" = %d; ",
+ds_put_format(_action, REG_ORIG_TP_DPORT" = %d; ",
   lb_vip->vip_port);
+ds_put_format(_action_learn, ipv6 ? "[%s]:%d" : "%s:%d",
+  lb_vip->vip_str, lb_vip->vip_port);
+} else {
+ds_put_cstr(_action_learn, lb_vip->vip_str);
 }
 
 if (lb_action) {
-ds_put_format(_action_lb_common, "%s;", lb_action);
+ds_put_cstr(_action, lb_action);
 }
+ds_put_cstr(_action, "ct_lb_mark(backends=");
+ds_put_cstr(_action_learn, "\", backend = \"");
+
+/* Prepare common part of affinity learn match. */
+ds_put_format(_match_learn, REGBIT_KNOWN_LB_SESSION" == 0 && "
+  "ct.new && %s && %s == %s && %s.dst == ", ip_match,
+  reg_vip, lb_vip->vip_str, ip_match);
+
+/* Prepare common part of affinity match. */
+ds_put_format(_match, REGBIT_KNOWN_LB_SESSION" == 1 && "
+  "ct.new && %s && %s == ", ip_match, reg_backend);
+
+/* Store the common part length. */
+size_t aff_action_len = aff_action.length;
+size_t aff_action_learn_len = aff_action_learn.length;
+size_t aff_match_len = aff_match.length;
+size_t aff_match_learn_len = aff_match_learn.length;
+
 
-stage0 =