diff mbox series

[ovs-dev,4/4] ovn-northd: Reduce amount of flow hashing.

Message ID 20180214215457.18110-4-blp@ovn.org
State Accepted
Headers show
Series [ovs-dev,1/4] ovsdb-idlc: Add "cDecls" and "hDecls" IDL schema extensions. | expand

Commit Message

Ben Pfaff Feb. 14, 2018, 9:54 p.m. UTC
Jakub Sitnicki demonstrated that repeatedly calculating row hashes is
expensive, so this should improve ovn-northd performance.

Reported-by: Jakub Sitnicki <jkbs@redhat.com>
Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2018-February/344404.html
Signed-off-by: Ben Pfaff <blp@ovn.org>
---
 ovn/lib/ovn-sb-idl.ann  | 20 ++++++++++++++++++++
 ovn/lib/ovn-util.c      | 27 +++++++++++++++++++++++++++
 ovn/lib/ovn-util.h      |  7 +++++++
 ovn/northd/ovn-northd.c | 28 +++++++++++++++++-----------
 4 files changed, 71 insertions(+), 11 deletions(-)

Comments

Jakub Sitnicki Feb. 16, 2018, 4:10 p.m. UTC | #1
Ben,

On Wed, Feb 14, 2018 at 09:54 PM GMT, Ben Pfaff wrote:
> Jakub Sitnicki demonstrated that repeatedly calculating row hashes is
> expensive, so this should improve ovn-northd performance.
>
> Reported-by: Jakub Sitnicki <jkbs@redhat.com>
> Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2018-February/344404.html
> Signed-off-by: Ben Pfaff <blp@ovn.org>
> ---
>  ovn/lib/ovn-sb-idl.ann  | 20 ++++++++++++++++++++
>  ovn/lib/ovn-util.c      | 27 +++++++++++++++++++++++++++
>  ovn/lib/ovn-util.h      |  7 +++++++
>  ovn/northd/ovn-northd.c | 28 +++++++++++++++++-----------
>  4 files changed, 71 insertions(+), 11 deletions(-)
>

With this series applied the total of CPU cycles consumed by northd and
the per-function profile changes dramatically when running a simple
benchmark of creating 15 lswitches, 100 lports per each lswitch.

`perf stat` for ovn-northd looks like so:

before:

 Performance counter stats for process id '7091':

      85263.043641      task-clock (msec)         #    0.843 CPUs utilized
             9,859      context-switches          #    0.116 K/sec
               575      cpu-migrations            #    0.007 K/sec
             2,243      page-faults               #    0.026 K/sec
   225,663,620,029      cycles                    #    2.647 GHz
   302,290,105,647      instructions              #    1.34  insn per cycle
    53,556,381,940      branches                  #  628.131 M/sec
       435,374,510      branch-misses             #    0.81% of all branches

     101.202109604 seconds time elapsed

after:

 Performance counter stats for process id '25306':

      50362.124282      task-clock (msec)         #    0.474 CPUs utilized
             5,120      context-switches          #    0.102 K/sec
             1,025      cpu-migrations            #    0.020 K/sec
             9,546      page-faults               #    0.190 K/sec
   134,756,308,237      cycles                    #    2.676 GHz
   154,810,279,583      instructions              #    1.15  insn per cycle
    27,350,003,179      branches                  #  543.067 M/sec
       216,219,142      branch-misses             #    0.79% of all branches

     106.207558198 seconds time elapsed

CPU time spent in ovn_lflow_find becomes insignificant (<0.1%) after the
changes, so instead let's look at the total cycles spent processing NBDB
contents:

  Children      Self  Command     Shared Object       Symbol
before:
    75.95%     0.01%  ovn-northd  ovn-northd          [.] ovnnb_db_run
after:
     8.55%     0.02%  ovn-northd  ovn-northd          [.] ovnnb_db_run

Honestly, I am not sure where the huge boost comes from. I need to read
through the changes more closely. Feel free to add my:

Tested-by: Jakub Sitnicki <jkbs@redhat.com>

Thanks,
Jakub

> diff --git a/ovn/lib/ovn-sb-idl.ann b/ovn/lib/ovn-sb-idl.ann
> index 2dfc64e3c4a7..e51238b92e97 100644
> --- a/ovn/lib/ovn-sb-idl.ann
> +++ b/ovn/lib/ovn-sb-idl.ann
> @@ -7,3 +7,23 @@
>
>  s["idlPrefix"] = "sbrec_"
>  s["idlHeader"] = "\"ovn/lib/ovn-sb-idl.h\""
> +
> +s["hDecls"] = '#include "ovn/lib/ovn-util.h"'
> +
> +# Adds an integer column named 'column' to 'table' in 's'.  The column
> +# values is calculated with 'expression' based on the values of the columns
> +# named in the array 'dependencies'.
> +def synthesize_integer_column(s, table, column, dependencies, expression):
> +    s["tables"][table]["columns"][column] = {
> +        "type": "integer",
> +        "extensions": {
> +            "dependencies": dependencies,
> +            "parse": "row->%s = %s;" % (column, expression),
> +            "synthetic": True
> +        }
> +    }
> +
> +synthesize_integer_column(s, "Logical_Flow", "hash",
> +                          ["logical_datapath", "table_id", "pipeline",
> +                           "priority", "match", "actions"],
> +                          "sbrec_logical_flow_hash(row)")
> diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c
> index a554c23f5ae8..e9464e926d74 100644
> --- a/ovn/lib/ovn-util.c
> +++ b/ovn/lib/ovn-util.c
> @@ -17,6 +17,7 @@
>  #include "dirs.h"
>  #include "openvswitch/vlog.h"
>  #include "ovn/lib/ovn-nb-idl.h"
> +#include "ovn/lib/ovn-sb-idl.h"
>
>  VLOG_DEFINE_THIS_MODULE(ovn_util);
>
> @@ -329,3 +330,29 @@ ovn_is_known_nb_lsp_type(const char *type)
>
>      return false;
>  }
> +
> +uint32_t
> +sbrec_logical_flow_hash(const struct sbrec_logical_flow *lf)
> +{
> +    const struct sbrec_datapath_binding *ld = lf->logical_datapath;
> +    if (!ld) {
> +        return 0;
> +    }
> +
> +    return ovn_logical_flow_hash(&ld->header_.uuid,
> +                                 lf->table_id, lf->pipeline,
> +                                 lf->priority, lf->match, lf->actions);
> +}
> +
> +uint32_t
> +ovn_logical_flow_hash(const struct uuid *logical_datapath,
> +                      uint8_t table_id, const char *pipeline,
> +                      uint16_t priority,
> +                      const char *match, const char *actions)
> +{
> +    size_t hash = uuid_hash(logical_datapath);
> +    hash = hash_2words((table_id << 16) | priority, hash);
> +    hash = hash_string(pipeline, hash);
> +    hash = hash_string(match, hash);
> +    return hash_string(actions, hash);
> +}
> diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
> index 9b456426dc67..7ff9f9a1c73b 100644
> --- a/ovn/lib/ovn-util.h
> +++ b/ovn/lib/ovn-util.h
> @@ -19,6 +19,7 @@
>  #include "lib/packets.h"
>
>  struct nbrec_logical_router_port;
> +struct sbrec_logical_flow;
>  struct uuid;
>
>  struct ipv4_netaddr {
> @@ -69,4 +70,10 @@ const char *default_sb_db(void);
>
>  bool ovn_is_known_nb_lsp_type(const char *type);
>
> +uint32_t sbrec_logical_flow_hash(const struct sbrec_logical_flow *);
> +uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
> +                               uint8_t table_id, const char *pipeline,
> +                               uint16_t priority,
> +                               const char *match, const char *actions);
> +
>  #endif
> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> index 4d95a3d9d40e..5d764f6e9404 100644
> --- a/ovn/northd/ovn-northd.c
> +++ b/ovn/northd/ovn-northd.c
> @@ -194,6 +194,13 @@ ovn_stage_get_pipeline(enum ovn_stage stage)
>      return (stage >> 8) & 1;
>  }
>
> +/* Returns the pipeline name to which 'stage' belongs. */
> +static const char *
> +ovn_stage_get_pipeline_name(enum ovn_stage stage)
> +{
> +    return ovn_stage_get_pipeline(stage) == P_IN ? "ingress" : "egress";
> +}
> +
>  /* Returns the table to which 'stage' belongs. */
>  static uint8_t
>  ovn_stage_get_table(enum ovn_stage stage)
> @@ -2271,10 +2278,11 @@ struct ovn_lflow {
>  static size_t
>  ovn_lflow_hash(const struct ovn_lflow *lflow)
>  {
> -    size_t hash = uuid_hash(&lflow->od->key);
> -    hash = hash_2words((lflow->stage << 16) | lflow->priority, hash);
> -    hash = hash_string(lflow->match, hash);
> -    return hash_string(lflow->actions, hash);
> +    return ovn_logical_flow_hash(&lflow->od->key,
> +                                 ovn_stage_get_table(lflow->stage),
> +                                 ovn_stage_get_pipeline_name(lflow->stage),
> +                                 lflow->priority, lflow->match,
> +                                 lflow->actions);
>  }
>
>  static bool
> @@ -2331,7 +2339,7 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
>  static struct ovn_lflow *
>  ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
>                 enum ovn_stage stage, uint16_t priority,
> -               const char *match, const char *actions)
> +               const char *match, const char *actions, uint32_t hash)
>  {
>      struct ovn_lflow target;
>      ovn_lflow_init(&target, od, stage, priority,
> @@ -2339,8 +2347,7 @@ ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
>                     NULL, NULL);
>
>      struct ovn_lflow *lflow;
> -    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, ovn_lflow_hash(&target),
> -                             lflows) {
> +    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) {
>          if (ovn_lflow_equal(lflow, &target)) {
>              return lflow;
>          }
> @@ -6014,7 +6021,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
>              = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
>          struct ovn_lflow *lflow = ovn_lflow_find(
>              &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id),
> -            sbflow->priority, sbflow->match, sbflow->actions);
> +            sbflow->priority, sbflow->match, sbflow->actions, sbflow->hash);
>          if (lflow) {
>              ovn_lflow_destroy(&lflows, lflow);
>          } else {
> @@ -6023,13 +6030,12 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
>      }
>      struct ovn_lflow *lflow, *next_lflow;
>      HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
> -        enum ovn_pipeline pipeline = ovn_stage_get_pipeline(lflow->stage);
> +        const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
>          uint8_t table = ovn_stage_get_table(lflow->stage);
>
>          sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
>          sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
> -        sbrec_logical_flow_set_pipeline(
> -            sbflow, pipeline == P_IN ? "ingress" : "egress");
> +        sbrec_logical_flow_set_pipeline(sbflow, pipeline);
>          sbrec_logical_flow_set_table_id(sbflow, table);
>          sbrec_logical_flow_set_priority(sbflow, lflow->priority);
>          sbrec_logical_flow_set_match(sbflow, lflow->match);
Ben Pfaff Feb. 16, 2018, 11:10 p.m. UTC | #2
On Fri, Feb 16, 2018 at 05:10:29PM +0100, Jakub Sitnicki wrote:
> Ben,
> 
> On Wed, Feb 14, 2018 at 09:54 PM GMT, Ben Pfaff wrote:
> > Jakub Sitnicki demonstrated that repeatedly calculating row hashes is
> > expensive, so this should improve ovn-northd performance.
> >
> > Reported-by: Jakub Sitnicki <jkbs@redhat.com>
> > Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2018-February/344404.html
> > Signed-off-by: Ben Pfaff <blp@ovn.org>
> > ---
> >  ovn/lib/ovn-sb-idl.ann  | 20 ++++++++++++++++++++
> >  ovn/lib/ovn-util.c      | 27 +++++++++++++++++++++++++++
> >  ovn/lib/ovn-util.h      |  7 +++++++
> >  ovn/northd/ovn-northd.c | 28 +++++++++++++++++-----------
> >  4 files changed, 71 insertions(+), 11 deletions(-)
> >
> 
> With this series applied the total of CPU cycles consumed by northd and
> the per-function profile changes dramatically when running a simple
> benchmark of creating 15 lswitches, 100 lports per each lswitch.
> 
> `perf stat` for ovn-northd looks like so:
> 
> before:
> 
>  Performance counter stats for process id '7091':
> 
>       85263.043641      task-clock (msec)         #    0.843 CPUs utilized
>              9,859      context-switches          #    0.116 K/sec
>                575      cpu-migrations            #    0.007 K/sec
>              2,243      page-faults               #    0.026 K/sec
>    225,663,620,029      cycles                    #    2.647 GHz
>    302,290,105,647      instructions              #    1.34  insn per cycle
>     53,556,381,940      branches                  #  628.131 M/sec
>        435,374,510      branch-misses             #    0.81% of all branches
> 
>      101.202109604 seconds time elapsed
> 
> after:
> 
>  Performance counter stats for process id '25306':
> 
>       50362.124282      task-clock (msec)         #    0.474 CPUs utilized
>              5,120      context-switches          #    0.102 K/sec
>              1,025      cpu-migrations            #    0.020 K/sec
>              9,546      page-faults               #    0.190 K/sec
>    134,756,308,237      cycles                    #    2.676 GHz
>    154,810,279,583      instructions              #    1.15  insn per cycle
>     27,350,003,179      branches                  #  543.067 M/sec
>        216,219,142      branch-misses             #    0.79% of all branches
> 
>      106.207558198 seconds time elapsed
> 
> CPU time spent in ovn_lflow_find becomes insignificant (<0.1%) after the
> changes, so instead let's look at the total cycles spent processing NBDB
> contents:
> 
>   Children      Self  Command     Shared Object       Symbol
> before:
>     75.95%     0.01%  ovn-northd  ovn-northd          [.] ovnnb_db_run
> after:
>      8.55%     0.02%  ovn-northd  ovn-northd          [.] ovnnb_db_run
> 
> Honestly, I am not sure where the huge boost comes from. I need to read
> through the changes more closely. Feel free to add my:
> 
> Tested-by: Jakub Sitnicki <jkbs@redhat.com>

Thanks a lot for the testing.  Please do feel free to continue reading
the patch series.  I am also surprised that there was so much impact--I
guess that hashing text strings is really expensive!

Thanks,

Ben.
Lorenzo Bianconi Feb. 20, 2018, 2:09 p.m. UTC | #3
> On Fri, Feb 16, 2018 at 05:10:29PM +0100, Jakub Sitnicki wrote:
>> Ben,
>>
>> On Wed, Feb 14, 2018 at 09:54 PM GMT, Ben Pfaff wrote:
>> > Jakub Sitnicki demonstrated that repeatedly calculating row hashes is
>> > expensive, so this should improve ovn-northd performance.
>> >
>> > Reported-by: Jakub Sitnicki <jkbs@redhat.com>
>> > Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2018-February/344404.html
>> > Signed-off-by: Ben Pfaff <blp@ovn.org>
>> > ---
>> >  ovn/lib/ovn-sb-idl.ann  | 20 ++++++++++++++++++++
>> >  ovn/lib/ovn-util.c      | 27 +++++++++++++++++++++++++++
>> >  ovn/lib/ovn-util.h      |  7 +++++++
>> >  ovn/northd/ovn-northd.c | 28 +++++++++++++++++-----------
>> >  4 files changed, 71 insertions(+), 11 deletions(-)
>> >
>>
>> With this series applied the total of CPU cycles consumed by northd and
>> the per-function profile changes dramatically when running a simple
>> benchmark of creating 15 lswitches, 100 lports per each lswitch.
>>
>> `perf stat` for ovn-northd looks like so:
>>
>> before:
>>
>>  Performance counter stats for process id '7091':
>>
>>       85263.043641      task-clock (msec)         #    0.843 CPUs utilized
>>              9,859      context-switches          #    0.116 K/sec
>>                575      cpu-migrations            #    0.007 K/sec
>>              2,243      page-faults               #    0.026 K/sec
>>    225,663,620,029      cycles                    #    2.647 GHz
>>    302,290,105,647      instructions              #    1.34  insn per cycle
>>     53,556,381,940      branches                  #  628.131 M/sec
>>        435,374,510      branch-misses             #    0.81% of all branches
>>
>>      101.202109604 seconds time elapsed
>>
>> after:
>>
>>  Performance counter stats for process id '25306':
>>
>>       50362.124282      task-clock (msec)         #    0.474 CPUs utilized
>>              5,120      context-switches          #    0.102 K/sec
>>              1,025      cpu-migrations            #    0.020 K/sec
>>              9,546      page-faults               #    0.190 K/sec
>>    134,756,308,237      cycles                    #    2.676 GHz
>>    154,810,279,583      instructions              #    1.15  insn per cycle
>>     27,350,003,179      branches                  #  543.067 M/sec
>>        216,219,142      branch-misses             #    0.79% of all branches
>>
>>      106.207558198 seconds time elapsed
>>
>> CPU time spent in ovn_lflow_find becomes insignificant (<0.1%) after the
>> changes, so instead let's look at the total cycles spent processing NBDB
>> contents:
>>
>>   Children      Self  Command     Shared Object       Symbol
>> before:
>>     75.95%     0.01%  ovn-northd  ovn-northd          [.] ovnnb_db_run
>> after:
>>      8.55%     0.02%  ovn-northd  ovn-northd          [.] ovnnb_db_run
>>
>> Honestly, I am not sure where the huge boost comes from. I need to read
>> through the changes more closely. Feel free to add my:
>>
>> Tested-by: Jakub Sitnicki <jkbs@redhat.com>
>
> Thanks a lot for the testing.  Please do feel free to continue reading
> the patch series.  I am also surprised that there was so much impact--I
> guess that hashing text strings is really expensive!
>
> Thanks,
>
> Ben.
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Hi Ben,

working on automatic test support for OVN ACL reject action I
discovered this commit breaks some ovn.at tests.
In particular:
- 2371: ovn -- dns lookup : 1 HV, 2 LS, 2 LSPs/LS
- 2374: ovn -- 1 LR with distributed router gateway port

I run git bisect on the current master branch (commit 430d7d156).
Numan reproduced same issues.
I guess they are probably timing issues

Regards,
Lorenzo
Mark Michelson Feb. 21, 2018, 11:19 p.m. UTC | #4
Hi,

While testing an unrelated change in OVS master (using make sandbox 
SANDBOXFLAGS="--ovn"), I noticed that my laptop was making way more 
noise than normal. Looking at `top` output, an ovsdb-server process was 
running at 100% CPU, ovn-controller was running at 75% CPU, and 
ovn-northd was running at about 38% CPU. If I start the ovs sandbox, the 
problem does not present itself until after I run the "ovn-setup.sh" 
script. As soon as I run the script, the CPU usage goes soaring.

If I revert to the patch just before this one ("implement synthetic 
columns"), the problem does not occur. At least in a sandbox 
environment, this patch appears to be causing some sort of busy loops 
that drive the CPU% of processes up. Looking at the change, it's not 
immediately obvious why this would be happening.

On 02/14/2018 03:54 PM, Ben Pfaff wrote:
> Jakub Sitnicki demonstrated that repeatedly calculating row hashes is
> expensive, so this should improve ovn-northd performance.
> 
> Reported-by: Jakub Sitnicki <jkbs@redhat.com>
> Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2018-February/344404.html
> Signed-off-by: Ben Pfaff <blp@ovn.org>
> ---
>   ovn/lib/ovn-sb-idl.ann  | 20 ++++++++++++++++++++
>   ovn/lib/ovn-util.c      | 27 +++++++++++++++++++++++++++
>   ovn/lib/ovn-util.h      |  7 +++++++
>   ovn/northd/ovn-northd.c | 28 +++++++++++++++++-----------
>   4 files changed, 71 insertions(+), 11 deletions(-)
> 
> diff --git a/ovn/lib/ovn-sb-idl.ann b/ovn/lib/ovn-sb-idl.ann
> index 2dfc64e3c4a7..e51238b92e97 100644
> --- a/ovn/lib/ovn-sb-idl.ann
> +++ b/ovn/lib/ovn-sb-idl.ann
> @@ -7,3 +7,23 @@
>   
>   s["idlPrefix"] = "sbrec_"
>   s["idlHeader"] = "\"ovn/lib/ovn-sb-idl.h\""
> +
> +s["hDecls"] = '#include "ovn/lib/ovn-util.h"'
> +
> +# Adds an integer column named 'column' to 'table' in 's'.  The column
> +# values is calculated with 'expression' based on the values of the columns
> +# named in the array 'dependencies'.
> +def synthesize_integer_column(s, table, column, dependencies, expression):
> +    s["tables"][table]["columns"][column] = {
> +        "type": "integer",
> +        "extensions": {
> +            "dependencies": dependencies,
> +            "parse": "row->%s = %s;" % (column, expression),
> +            "synthetic": True
> +        }
> +    }
> +
> +synthesize_integer_column(s, "Logical_Flow", "hash",
> +                          ["logical_datapath", "table_id", "pipeline",
> +                           "priority", "match", "actions"],
> +                          "sbrec_logical_flow_hash(row)")
> diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c
> index a554c23f5ae8..e9464e926d74 100644
> --- a/ovn/lib/ovn-util.c
> +++ b/ovn/lib/ovn-util.c
> @@ -17,6 +17,7 @@
>   #include "dirs.h"
>   #include "openvswitch/vlog.h"
>   #include "ovn/lib/ovn-nb-idl.h"
> +#include "ovn/lib/ovn-sb-idl.h"
>   
>   VLOG_DEFINE_THIS_MODULE(ovn_util);
>   
> @@ -329,3 +330,29 @@ ovn_is_known_nb_lsp_type(const char *type)
>   
>       return false;
>   }
> +
> +uint32_t
> +sbrec_logical_flow_hash(const struct sbrec_logical_flow *lf)
> +{
> +    const struct sbrec_datapath_binding *ld = lf->logical_datapath;
> +    if (!ld) {
> +        return 0;
> +    }
> +
> +    return ovn_logical_flow_hash(&ld->header_.uuid,
> +                                 lf->table_id, lf->pipeline,
> +                                 lf->priority, lf->match, lf->actions);
> +}
> +
> +uint32_t
> +ovn_logical_flow_hash(const struct uuid *logical_datapath,
> +                      uint8_t table_id, const char *pipeline,
> +                      uint16_t priority,
> +                      const char *match, const char *actions)
> +{
> +    size_t hash = uuid_hash(logical_datapath);
> +    hash = hash_2words((table_id << 16) | priority, hash);
> +    hash = hash_string(pipeline, hash);
> +    hash = hash_string(match, hash);
> +    return hash_string(actions, hash);
> +}
> diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
> index 9b456426dc67..7ff9f9a1c73b 100644
> --- a/ovn/lib/ovn-util.h
> +++ b/ovn/lib/ovn-util.h
> @@ -19,6 +19,7 @@
>   #include "lib/packets.h"
>   
>   struct nbrec_logical_router_port;
> +struct sbrec_logical_flow;
>   struct uuid;
>   
>   struct ipv4_netaddr {
> @@ -69,4 +70,10 @@ const char *default_sb_db(void);
>   
>   bool ovn_is_known_nb_lsp_type(const char *type);
>   
> +uint32_t sbrec_logical_flow_hash(const struct sbrec_logical_flow *);
> +uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
> +                               uint8_t table_id, const char *pipeline,
> +                               uint16_t priority,
> +                               const char *match, const char *actions);
> +
>   #endif
> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> index 4d95a3d9d40e..5d764f6e9404 100644
> --- a/ovn/northd/ovn-northd.c
> +++ b/ovn/northd/ovn-northd.c
> @@ -194,6 +194,13 @@ ovn_stage_get_pipeline(enum ovn_stage stage)
>       return (stage >> 8) & 1;
>   }
>   
> +/* Returns the pipeline name to which 'stage' belongs. */
> +static const char *
> +ovn_stage_get_pipeline_name(enum ovn_stage stage)
> +{
> +    return ovn_stage_get_pipeline(stage) == P_IN ? "ingress" : "egress";
> +}
> +
>   /* Returns the table to which 'stage' belongs. */
>   static uint8_t
>   ovn_stage_get_table(enum ovn_stage stage)
> @@ -2271,10 +2278,11 @@ struct ovn_lflow {
>   static size_t
>   ovn_lflow_hash(const struct ovn_lflow *lflow)
>   {
> -    size_t hash = uuid_hash(&lflow->od->key);
> -    hash = hash_2words((lflow->stage << 16) | lflow->priority, hash);
> -    hash = hash_string(lflow->match, hash);
> -    return hash_string(lflow->actions, hash);
> +    return ovn_logical_flow_hash(&lflow->od->key,
> +                                 ovn_stage_get_table(lflow->stage),
> +                                 ovn_stage_get_pipeline_name(lflow->stage),
> +                                 lflow->priority, lflow->match,
> +                                 lflow->actions);
>   }
>   
>   static bool
> @@ -2331,7 +2339,7 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
>   static struct ovn_lflow *
>   ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
>                  enum ovn_stage stage, uint16_t priority,
> -               const char *match, const char *actions)
> +               const char *match, const char *actions, uint32_t hash)
>   {
>       struct ovn_lflow target;
>       ovn_lflow_init(&target, od, stage, priority,
> @@ -2339,8 +2347,7 @@ ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
>                      NULL, NULL);
>   
>       struct ovn_lflow *lflow;
> -    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, ovn_lflow_hash(&target),
> -                             lflows) {
> +    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) {
>           if (ovn_lflow_equal(lflow, &target)) {
>               return lflow;
>           }
> @@ -6014,7 +6021,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
>               = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
>           struct ovn_lflow *lflow = ovn_lflow_find(
>               &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id),
> -            sbflow->priority, sbflow->match, sbflow->actions);
> +            sbflow->priority, sbflow->match, sbflow->actions, sbflow->hash);
>           if (lflow) {
>               ovn_lflow_destroy(&lflows, lflow);
>           } else {
> @@ -6023,13 +6030,12 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
>       }
>       struct ovn_lflow *lflow, *next_lflow;
>       HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
> -        enum ovn_pipeline pipeline = ovn_stage_get_pipeline(lflow->stage);
> +        const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
>           uint8_t table = ovn_stage_get_table(lflow->stage);
>   
>           sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
>           sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
> -        sbrec_logical_flow_set_pipeline(
> -            sbflow, pipeline == P_IN ? "ingress" : "egress");
> +        sbrec_logical_flow_set_pipeline(sbflow, pipeline);
>           sbrec_logical_flow_set_table_id(sbflow, table);
>           sbrec_logical_flow_set_priority(sbflow, lflow->priority);
>           sbrec_logical_flow_set_match(sbflow, lflow->match);
>
Numan Siddique Feb. 22, 2018, 4:29 a.m. UTC | #5
On Thu, Feb 22, 2018 at 4:49 AM, Mark Michelson <mmichels@redhat.com> wrote:

> Hi,
>
> While testing an unrelated change in OVS master (using make sandbox
> SANDBOXFLAGS="--ovn"), I noticed that my laptop was making way more noise
> than normal. Looking at `top` output, an ovsdb-server process was running
> at 100% CPU, ovn-controller was running at 75% CPU, and ovn-northd was
> running at about 38% CPU. If I start the ovs sandbox, the problem does not
> present itself until after I run the "ovn-setup.sh" script. As soon as I
> run the script, the CPU usage goes soaring.
>
> If I revert to the patch just before this one ("implement synthetic
> columns"), the problem does not occur. At least in a sandbox environment,
> this patch appears to be causing some sort of busy loops that drive the
> CPU% of processes up. Looking at the change, it's not immediately obvious
> why this would be happening.
>
>
I can confirm that I am seeing the same with my sandbox environment. OVN
North db ovsdb-server  is almost 100% and ovn-controller at around 90% in
my case.

Thanks
Numan


> On 02/14/2018 03:54 PM, Ben Pfaff wrote:
>
>> Jakub Sitnicki demonstrated that repeatedly calculating row hashes is
>> expensive, so this should improve ovn-northd performance.
>>
>> Reported-by: Jakub Sitnicki <jkbs@redhat.com>
>> Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2018-February
>> /344404.html
>> Signed-off-by: Ben Pfaff <blp@ovn.org>
>> ---
>>   ovn/lib/ovn-sb-idl.ann  | 20 ++++++++++++++++++++
>>   ovn/lib/ovn-util.c      | 27 +++++++++++++++++++++++++++
>>   ovn/lib/ovn-util.h      |  7 +++++++
>>   ovn/northd/ovn-northd.c | 28 +++++++++++++++++-----------
>>   4 files changed, 71 insertions(+), 11 deletions(-)
>>
>> diff --git a/ovn/lib/ovn-sb-idl.ann b/ovn/lib/ovn-sb-idl.ann
>> index 2dfc64e3c4a7..e51238b92e97 100644
>> --- a/ovn/lib/ovn-sb-idl.ann
>> +++ b/ovn/lib/ovn-sb-idl.ann
>> @@ -7,3 +7,23 @@
>>     s["idlPrefix"] = "sbrec_"
>>   s["idlHeader"] = "\"ovn/lib/ovn-sb-idl.h\""
>> +
>> +s["hDecls"] = '#include "ovn/lib/ovn-util.h"'
>> +
>> +# Adds an integer column named 'column' to 'table' in 's'.  The column
>> +# values is calculated with 'expression' based on the values of the
>> columns
>> +# named in the array 'dependencies'.
>> +def synthesize_integer_column(s, table, column, dependencies,
>> expression):
>> +    s["tables"][table]["columns"][column] = {
>> +        "type": "integer",
>> +        "extensions": {
>> +            "dependencies": dependencies,
>> +            "parse": "row->%s = %s;" % (column, expression),
>> +            "synthetic": True
>> +        }
>> +    }
>> +
>> +synthesize_integer_column(s, "Logical_Flow", "hash",
>> +                          ["logical_datapath", "table_id", "pipeline",
>> +                           "priority", "match", "actions"],
>> +                          "sbrec_logical_flow_hash(row)")
>> diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c
>> index a554c23f5ae8..e9464e926d74 100644
>> --- a/ovn/lib/ovn-util.c
>> +++ b/ovn/lib/ovn-util.c
>> @@ -17,6 +17,7 @@
>>   #include "dirs.h"
>>   #include "openvswitch/vlog.h"
>>   #include "ovn/lib/ovn-nb-idl.h"
>> +#include "ovn/lib/ovn-sb-idl.h"
>>     VLOG_DEFINE_THIS_MODULE(ovn_util);
>>   @@ -329,3 +330,29 @@ ovn_is_known_nb_lsp_type(const char *type)
>>         return false;
>>   }
>> +
>> +uint32_t
>> +sbrec_logical_flow_hash(const struct sbrec_logical_flow *lf)
>> +{
>> +    const struct sbrec_datapath_binding *ld = lf->logical_datapath;
>> +    if (!ld) {
>> +        return 0;
>> +    }
>> +
>> +    return ovn_logical_flow_hash(&ld->header_.uuid,
>> +                                 lf->table_id, lf->pipeline,
>> +                                 lf->priority, lf->match, lf->actions);
>> +}
>> +
>> +uint32_t
>> +ovn_logical_flow_hash(const struct uuid *logical_datapath,
>> +                      uint8_t table_id, const char *pipeline,
>> +                      uint16_t priority,
>> +                      const char *match, const char *actions)
>> +{
>> +    size_t hash = uuid_hash(logical_datapath);
>> +    hash = hash_2words((table_id << 16) | priority, hash);
>> +    hash = hash_string(pipeline, hash);
>> +    hash = hash_string(match, hash);
>> +    return hash_string(actions, hash);
>> +}
>> diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
>> index 9b456426dc67..7ff9f9a1c73b 100644
>> --- a/ovn/lib/ovn-util.h
>> +++ b/ovn/lib/ovn-util.h
>> @@ -19,6 +19,7 @@
>>   #include "lib/packets.h"
>>     struct nbrec_logical_router_port;
>> +struct sbrec_logical_flow;
>>   struct uuid;
>>     struct ipv4_netaddr {
>> @@ -69,4 +70,10 @@ const char *default_sb_db(void);
>>     bool ovn_is_known_nb_lsp_type(const char *type);
>>   +uint32_t sbrec_logical_flow_hash(const struct sbrec_logical_flow *);
>> +uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
>> +                               uint8_t table_id, const char *pipeline,
>> +                               uint16_t priority,
>> +                               const char *match, const char *actions);
>> +
>>   #endif
>> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
>> index 4d95a3d9d40e..5d764f6e9404 100644
>> --- a/ovn/northd/ovn-northd.c
>> +++ b/ovn/northd/ovn-northd.c
>> @@ -194,6 +194,13 @@ ovn_stage_get_pipeline(enum ovn_stage stage)
>>       return (stage >> 8) & 1;
>>   }
>>   +/* Returns the pipeline name to which 'stage' belongs. */
>> +static const char *
>> +ovn_stage_get_pipeline_name(enum ovn_stage stage)
>> +{
>> +    return ovn_stage_get_pipeline(stage) == P_IN ? "ingress" : "egress";
>> +}
>> +
>>   /* Returns the table to which 'stage' belongs. */
>>   static uint8_t
>>   ovn_stage_get_table(enum ovn_stage stage)
>> @@ -2271,10 +2278,11 @@ struct ovn_lflow {
>>   static size_t
>>   ovn_lflow_hash(const struct ovn_lflow *lflow)
>>   {
>> -    size_t hash = uuid_hash(&lflow->od->key);
>> -    hash = hash_2words((lflow->stage << 16) | lflow->priority, hash);
>> -    hash = hash_string(lflow->match, hash);
>> -    return hash_string(lflow->actions, hash);
>> +    return ovn_logical_flow_hash(&lflow->od->key,
>> +                                 ovn_stage_get_table(lflow->stage),
>> +                                 ovn_stage_get_pipeline_name(l
>> flow->stage),
>> +                                 lflow->priority, lflow->match,
>> +                                 lflow->actions);
>>   }
>>     static bool
>> @@ -2331,7 +2339,7 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct
>> ovn_datapath *od,
>>   static struct ovn_lflow *
>>   ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
>>                  enum ovn_stage stage, uint16_t priority,
>> -               const char *match, const char *actions)
>> +               const char *match, const char *actions, uint32_t hash)
>>   {
>>       struct ovn_lflow target;
>>       ovn_lflow_init(&target, od, stage, priority,
>> @@ -2339,8 +2347,7 @@ ovn_lflow_find(struct hmap *lflows, struct
>> ovn_datapath *od,
>>                      NULL, NULL);
>>         struct ovn_lflow *lflow;
>> -    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, ovn_lflow_hash(&target),
>> -                             lflows) {
>> +    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) {
>>           if (ovn_lflow_equal(lflow, &target)) {
>>               return lflow;
>>           }
>> @@ -6014,7 +6021,7 @@ build_lflows(struct northd_context *ctx, struct
>> hmap *datapaths,
>>               = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
>>           struct ovn_lflow *lflow = ovn_lflow_find(
>>               &lflows, od, ovn_stage_build(dp_type, pipeline,
>> sbflow->table_id),
>> -            sbflow->priority, sbflow->match, sbflow->actions);
>> +            sbflow->priority, sbflow->match, sbflow->actions,
>> sbflow->hash);
>>           if (lflow) {
>>               ovn_lflow_destroy(&lflows, lflow);
>>           } else {
>> @@ -6023,13 +6030,12 @@ build_lflows(struct northd_context *ctx, struct
>> hmap *datapaths,
>>       }
>>       struct ovn_lflow *lflow, *next_lflow;
>>       HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
>> -        enum ovn_pipeline pipeline = ovn_stage_get_pipeline(lflow->
>> stage);
>> +        const char *pipeline = ovn_stage_get_pipeline_name(lf
>> low->stage);
>>           uint8_t table = ovn_stage_get_table(lflow->stage);
>>             sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
>>           sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
>> -        sbrec_logical_flow_set_pipeline(
>> -            sbflow, pipeline == P_IN ? "ingress" : "egress");
>> +        sbrec_logical_flow_set_pipeline(sbflow, pipeline);
>>           sbrec_logical_flow_set_table_id(sbflow, table);
>>           sbrec_logical_flow_set_priority(sbflow, lflow->priority);
>>           sbrec_logical_flow_set_match(sbflow, lflow->match);
>>
>>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Jakub Sitnicki Feb. 22, 2018, 5:07 p.m. UTC | #6
Hi Ben,

On Wed, Feb 14, 2018 at 09:54 PM GMT, Ben Pfaff wrote:
> Jakub Sitnicki demonstrated that repeatedly calculating row hashes is
> expensive, so this should improve ovn-northd performance.
>
> Reported-by: Jakub Sitnicki <jkbs@redhat.com>
> Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2018-February/344404.html
> Signed-off-by: Ben Pfaff <blp@ovn.org>
> ---
>  ovn/lib/ovn-sb-idl.ann  | 20 ++++++++++++++++++++
>  ovn/lib/ovn-util.c      | 27 +++++++++++++++++++++++++++
>  ovn/lib/ovn-util.h      |  7 +++++++
>  ovn/northd/ovn-northd.c | 28 +++++++++++++++++-----------
>  4 files changed, 71 insertions(+), 11 deletions(-)
>
> diff --git a/ovn/lib/ovn-sb-idl.ann b/ovn/lib/ovn-sb-idl.ann
> index 2dfc64e3c4a7..e51238b92e97 100644
> --- a/ovn/lib/ovn-sb-idl.ann
> +++ b/ovn/lib/ovn-sb-idl.ann
> @@ -7,3 +7,23 @@
>
>  s["idlPrefix"] = "sbrec_"
>  s["idlHeader"] = "\"ovn/lib/ovn-sb-idl.h\""
> +
> +s["hDecls"] = '#include "ovn/lib/ovn-util.h"'
> +
> +# Adds an integer column named 'column' to 'table' in 's'.  The column
> +# values is calculated with 'expression' based on the values of the columns
> +# named in the array 'dependencies'.
> +def synthesize_integer_column(s, table, column, dependencies, expression):
> +    s["tables"][table]["columns"][column] = {
> +        "type": "integer",
> +        "extensions": {
> +            "dependencies": dependencies,
> +            "parse": "row->%s = %s;" % (column, expression),
> +            "synthetic": True
> +        }
> +    }
> +
> +synthesize_integer_column(s, "Logical_Flow", "hash",
> +                          ["logical_datapath", "table_id", "pipeline",
> +                           "priority", "match", "actions"],
> +                          "sbrec_logical_flow_hash(row)")
> diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c
> index a554c23f5ae8..e9464e926d74 100644
> --- a/ovn/lib/ovn-util.c
> +++ b/ovn/lib/ovn-util.c
> @@ -17,6 +17,7 @@
>  #include "dirs.h"
>  #include "openvswitch/vlog.h"
>  #include "ovn/lib/ovn-nb-idl.h"
> +#include "ovn/lib/ovn-sb-idl.h"
>
>  VLOG_DEFINE_THIS_MODULE(ovn_util);
>
> @@ -329,3 +330,29 @@ ovn_is_known_nb_lsp_type(const char *type)
>
>      return false;
>  }
> +
> +uint32_t
> +sbrec_logical_flow_hash(const struct sbrec_logical_flow *lf)
> +{
> +    const struct sbrec_datapath_binding *ld = lf->logical_datapath;
> +    if (!ld) {
> +        return 0;
> +    }
> +
> +    return ovn_logical_flow_hash(&ld->header_.uuid,
> +                                 lf->table_id, lf->pipeline,
> +                                 lf->priority, lf->match, lf->actions);
> +}

It looks like we should be hashing the logical datapath UUID here,
instead of the UUID of the entry in Datapath_Binding. Right now, the
hash computed from SB record will never match the hash computed from
NBDB contents.

This leads to northd contantly dropping records from SBDB and inserting
them again and again. I believe this is the root cause of problems that
Mark and Numan are reporting.

-Jakub

> +
> +uint32_t
> +ovn_logical_flow_hash(const struct uuid *logical_datapath,
> +                      uint8_t table_id, const char *pipeline,
> +                      uint16_t priority,
> +                      const char *match, const char *actions)
> +{
> +    size_t hash = uuid_hash(logical_datapath);
> +    hash = hash_2words((table_id << 16) | priority, hash);
> +    hash = hash_string(pipeline, hash);
> +    hash = hash_string(match, hash);
> +    return hash_string(actions, hash);
> +}
> diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
> index 9b456426dc67..7ff9f9a1c73b 100644
> --- a/ovn/lib/ovn-util.h
> +++ b/ovn/lib/ovn-util.h
> @@ -19,6 +19,7 @@
>  #include "lib/packets.h"
>
>  struct nbrec_logical_router_port;
> +struct sbrec_logical_flow;
>  struct uuid;
>
>  struct ipv4_netaddr {
> @@ -69,4 +70,10 @@ const char *default_sb_db(void);
>
>  bool ovn_is_known_nb_lsp_type(const char *type);
>
> +uint32_t sbrec_logical_flow_hash(const struct sbrec_logical_flow *);
> +uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
> +                               uint8_t table_id, const char *pipeline,
> +                               uint16_t priority,
> +                               const char *match, const char *actions);
> +
>  #endif
> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> index 4d95a3d9d40e..5d764f6e9404 100644
> --- a/ovn/northd/ovn-northd.c
> +++ b/ovn/northd/ovn-northd.c
> @@ -194,6 +194,13 @@ ovn_stage_get_pipeline(enum ovn_stage stage)
>      return (stage >> 8) & 1;
>  }
>
> +/* Returns the pipeline name to which 'stage' belongs. */
> +static const char *
> +ovn_stage_get_pipeline_name(enum ovn_stage stage)
> +{
> +    return ovn_stage_get_pipeline(stage) == P_IN ? "ingress" : "egress";
> +}
> +
>  /* Returns the table to which 'stage' belongs. */
>  static uint8_t
>  ovn_stage_get_table(enum ovn_stage stage)
> @@ -2271,10 +2278,11 @@ struct ovn_lflow {
>  static size_t
>  ovn_lflow_hash(const struct ovn_lflow *lflow)
>  {
> -    size_t hash = uuid_hash(&lflow->od->key);
> -    hash = hash_2words((lflow->stage << 16) | lflow->priority, hash);
> -    hash = hash_string(lflow->match, hash);
> -    return hash_string(lflow->actions, hash);
> +    return ovn_logical_flow_hash(&lflow->od->key,
> +                                 ovn_stage_get_table(lflow->stage),
> +                                 ovn_stage_get_pipeline_name(lflow->stage),
> +                                 lflow->priority, lflow->match,
> +                                 lflow->actions);
>  }
>
>  static bool
> @@ -2331,7 +2339,7 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
>  static struct ovn_lflow *
>  ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
>                 enum ovn_stage stage, uint16_t priority,
> -               const char *match, const char *actions)
> +               const char *match, const char *actions, uint32_t hash)
>  {
>      struct ovn_lflow target;
>      ovn_lflow_init(&target, od, stage, priority,
> @@ -2339,8 +2347,7 @@ ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
>                     NULL, NULL);
>
>      struct ovn_lflow *lflow;
> -    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, ovn_lflow_hash(&target),
> -                             lflows) {
> +    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) {
>          if (ovn_lflow_equal(lflow, &target)) {
>              return lflow;
>          }
> @@ -6014,7 +6021,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
>              = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
>          struct ovn_lflow *lflow = ovn_lflow_find(
>              &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id),
> -            sbflow->priority, sbflow->match, sbflow->actions);
> +            sbflow->priority, sbflow->match, sbflow->actions, sbflow->hash);
>          if (lflow) {
>              ovn_lflow_destroy(&lflows, lflow);
>          } else {
> @@ -6023,13 +6030,12 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
>      }
>      struct ovn_lflow *lflow, *next_lflow;
>      HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
> -        enum ovn_pipeline pipeline = ovn_stage_get_pipeline(lflow->stage);
> +        const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
>          uint8_t table = ovn_stage_get_table(lflow->stage);
>
>          sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
>          sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
> -        sbrec_logical_flow_set_pipeline(
> -            sbflow, pipeline == P_IN ? "ingress" : "egress");
> +        sbrec_logical_flow_set_pipeline(sbflow, pipeline);
>          sbrec_logical_flow_set_table_id(sbflow, table);
>          sbrec_logical_flow_set_priority(sbflow, lflow->priority);
>          sbrec_logical_flow_set_match(sbflow, lflow->match);
Jakub Sitnicki Feb. 23, 2018, 10:14 a.m. UTC | #7
On Thu, 22 Feb 2018 18:07:55 +0100
Jakub Sitnicki <jkbs@redhat.com> wrote:

> On Wed, Feb 14, 2018 at 09:54 PM GMT, Ben Pfaff wrote:

[...]

> > +uint32_t
> > +sbrec_logical_flow_hash(const struct sbrec_logical_flow *lf)
> > +{
> > +    const struct sbrec_datapath_binding *ld = lf->logical_datapath;
> > +    if (!ld) {
> > +        return 0;
> > +    }
> > +
> > +    return ovn_logical_flow_hash(&ld->header_.uuid,
> > +                                 lf->table_id, lf->pipeline,
> > +                                 lf->priority, lf->match, lf->actions);
> > +}  
> 
> It looks like we should be hashing the logical datapath UUID here,
> instead of the UUID of the entry in Datapath_Binding. Right now, the
> hash computed from SB record will never match the hash computed from
> NBDB contents.
> 
> This leads to northd contantly dropping records from SBDB and inserting
> them again and again. I believe this is the root cause of problems that
> Mark and Numan are reporting.

Proposed fix now posted: https://patchwork.ozlabs.org/patch/876842/

Thanks,
Jakub
diff mbox series

Patch

diff --git a/ovn/lib/ovn-sb-idl.ann b/ovn/lib/ovn-sb-idl.ann
index 2dfc64e3c4a7..e51238b92e97 100644
--- a/ovn/lib/ovn-sb-idl.ann
+++ b/ovn/lib/ovn-sb-idl.ann
@@ -7,3 +7,23 @@ 
 
 s["idlPrefix"] = "sbrec_"
 s["idlHeader"] = "\"ovn/lib/ovn-sb-idl.h\""
+
+s["hDecls"] = '#include "ovn/lib/ovn-util.h"'
+
+# Adds an integer column named 'column' to 'table' in 's'.  The column
+# values is calculated with 'expression' based on the values of the columns
+# named in the array 'dependencies'.
+def synthesize_integer_column(s, table, column, dependencies, expression):
+    s["tables"][table]["columns"][column] = {
+        "type": "integer",
+        "extensions": {
+            "dependencies": dependencies,
+            "parse": "row->%s = %s;" % (column, expression),
+            "synthetic": True
+        }
+    }
+
+synthesize_integer_column(s, "Logical_Flow", "hash",
+                          ["logical_datapath", "table_id", "pipeline",
+                           "priority", "match", "actions"],
+                          "sbrec_logical_flow_hash(row)")
diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c
index a554c23f5ae8..e9464e926d74 100644
--- a/ovn/lib/ovn-util.c
+++ b/ovn/lib/ovn-util.c
@@ -17,6 +17,7 @@ 
 #include "dirs.h"
 #include "openvswitch/vlog.h"
 #include "ovn/lib/ovn-nb-idl.h"
+#include "ovn/lib/ovn-sb-idl.h"
 
 VLOG_DEFINE_THIS_MODULE(ovn_util);
 
@@ -329,3 +330,29 @@  ovn_is_known_nb_lsp_type(const char *type)
 
     return false;
 }
+
+uint32_t
+sbrec_logical_flow_hash(const struct sbrec_logical_flow *lf)
+{
+    const struct sbrec_datapath_binding *ld = lf->logical_datapath;
+    if (!ld) {
+        return 0;
+    }
+
+    return ovn_logical_flow_hash(&ld->header_.uuid,
+                                 lf->table_id, lf->pipeline,
+                                 lf->priority, lf->match, lf->actions);
+}
+
+uint32_t
+ovn_logical_flow_hash(const struct uuid *logical_datapath,
+                      uint8_t table_id, const char *pipeline,
+                      uint16_t priority,
+                      const char *match, const char *actions)
+{
+    size_t hash = uuid_hash(logical_datapath);
+    hash = hash_2words((table_id << 16) | priority, hash);
+    hash = hash_string(pipeline, hash);
+    hash = hash_string(match, hash);
+    return hash_string(actions, hash);
+}
diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
index 9b456426dc67..7ff9f9a1c73b 100644
--- a/ovn/lib/ovn-util.h
+++ b/ovn/lib/ovn-util.h
@@ -19,6 +19,7 @@ 
 #include "lib/packets.h"
 
 struct nbrec_logical_router_port;
+struct sbrec_logical_flow;
 struct uuid;
 
 struct ipv4_netaddr {
@@ -69,4 +70,10 @@  const char *default_sb_db(void);
 
 bool ovn_is_known_nb_lsp_type(const char *type);
 
+uint32_t sbrec_logical_flow_hash(const struct sbrec_logical_flow *);
+uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
+                               uint8_t table_id, const char *pipeline,
+                               uint16_t priority,
+                               const char *match, const char *actions);
+
 #endif
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 4d95a3d9d40e..5d764f6e9404 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -194,6 +194,13 @@  ovn_stage_get_pipeline(enum ovn_stage stage)
     return (stage >> 8) & 1;
 }
 
+/* Returns the pipeline name to which 'stage' belongs. */
+static const char *
+ovn_stage_get_pipeline_name(enum ovn_stage stage)
+{
+    return ovn_stage_get_pipeline(stage) == P_IN ? "ingress" : "egress";
+}
+
 /* Returns the table to which 'stage' belongs. */
 static uint8_t
 ovn_stage_get_table(enum ovn_stage stage)
@@ -2271,10 +2278,11 @@  struct ovn_lflow {
 static size_t
 ovn_lflow_hash(const struct ovn_lflow *lflow)
 {
-    size_t hash = uuid_hash(&lflow->od->key);
-    hash = hash_2words((lflow->stage << 16) | lflow->priority, hash);
-    hash = hash_string(lflow->match, hash);
-    return hash_string(lflow->actions, hash);
+    return ovn_logical_flow_hash(&lflow->od->key,
+                                 ovn_stage_get_table(lflow->stage),
+                                 ovn_stage_get_pipeline_name(lflow->stage),
+                                 lflow->priority, lflow->match,
+                                 lflow->actions);
 }
 
 static bool
@@ -2331,7 +2339,7 @@  ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
 static struct ovn_lflow *
 ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
                enum ovn_stage stage, uint16_t priority,
-               const char *match, const char *actions)
+               const char *match, const char *actions, uint32_t hash)
 {
     struct ovn_lflow target;
     ovn_lflow_init(&target, od, stage, priority,
@@ -2339,8 +2347,7 @@  ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
                    NULL, NULL);
 
     struct ovn_lflow *lflow;
-    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, ovn_lflow_hash(&target),
-                             lflows) {
+    HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) {
         if (ovn_lflow_equal(lflow, &target)) {
             return lflow;
         }
@@ -6014,7 +6021,7 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
             = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
         struct ovn_lflow *lflow = ovn_lflow_find(
             &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id),
-            sbflow->priority, sbflow->match, sbflow->actions);
+            sbflow->priority, sbflow->match, sbflow->actions, sbflow->hash);
         if (lflow) {
             ovn_lflow_destroy(&lflows, lflow);
         } else {
@@ -6023,13 +6030,12 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
     }
     struct ovn_lflow *lflow, *next_lflow;
     HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
-        enum ovn_pipeline pipeline = ovn_stage_get_pipeline(lflow->stage);
+        const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
         uint8_t table = ovn_stage_get_table(lflow->stage);
 
         sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
         sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
-        sbrec_logical_flow_set_pipeline(
-            sbflow, pipeline == P_IN ? "ingress" : "egress");
+        sbrec_logical_flow_set_pipeline(sbflow, pipeline);
         sbrec_logical_flow_set_table_id(sbflow, table);
         sbrec_logical_flow_set_priority(sbflow, lflow->priority);
         sbrec_logical_flow_set_match(sbflow, lflow->match);